Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to simplify nested generics in C#?

Tags:

c#

generics

I love generics in C#, but sometimes working with them can get a bit complicated. The problem below I run into every now and then. Is there any way of making this scenario simpler? I can't see how, but I'm hoping someone can :)

Given three base classes:

public abstract class Inner
{
}

public abstract class Outer<T>
    where T : Inner
{
}

public abstract class Handler<TOuter, TInner>
    where TOuter : Outer<TInner>
    where TInner : Inner
{
    public abstract void SetValue(TInner value);
}

And some simple implementations:

public class In : Inner
{
}

public class Out : Outer<In>
{
}

public class HandleOut : Handler<Out, In>
{
    public override void SetValue(In value) { }
}

Now my question is: For HandleOut, the type of TInner is given by the type "Out", so is there any way to simplify the definition of HandleOut to something like public class HandleOut : Handler<Out> and still be able to use the inner type as a parameter to SetValue?

This is a very simple example, but I sometimes get a long list of generic types in definitions, when usually all of them can be logically deduced from the first type. Are there any tricks I'm missing?

like image 532
Christian Rygg Avatar asked Dec 29 '15 06:12

Christian Rygg


1 Answers

No.

While such inference should probably be possible, it is not part of the languge. You may be interested in suggesting this to Roslyn (open a new issue). Of course this type of generic constraints inference may run into problem in complex cases, but at least for the simple ones it is doable... still, is that where the C# team should put his time and effort?

The link Why are generic constraints not inherited that Damien_The_Unbeliever shared on the comments is spot on.


Anyway, in the code your presented while it is true that Out already give you the type In, the generic parameter TOuter is not needed.

The following code works equally well:

public abstract class Inner
{
}

public abstract class Outer<T>
    where T : Inner
{
}

public abstract class Handler<TInner> // NOTE: TOuter removed
    where TInner : Inner
{
    public abstract void SetValue(TInner value);
}

public class In : Inner
{
}

public class Out : Outer<In>
{
}

public class HandleOut : Handler<In> // NOTE: Out removed
{
    public override void SetValue(In value) { }
}

So, may consider using Outer<TInner> instead of TOuter if you need it. Of course, if you are keeping a list of TOuter it will be less restrictive as it will allow any derived type of Outer<TInner> instead of any derived type of TOuter.

Since you didn't put "new" at the generic constraint you are not creating objects of this type, but if the case arrives, you may accept a Func<Outer<TInner>> in the constructor.

like image 101
Theraot Avatar answered Oct 21 '22 16:10

Theraot