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?
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With