6

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?

3
  • 2
    I fixed your code so that it compiles. Can you please check to see if it's correct from your question's POV? Commented Dec 29, 2015 at 6:53
  • 4
    Whilst not 100% aligned with what I think you're asking, you might want to read a blog post by Eric Lippert titled Why are generic constraints not inherited (and, indeed, the fact that they're not inherited is why Enigmativity had to fix your code to add more constraints) Commented Dec 29, 2015 at 7:37
  • Thank you, @Enigmativity. I just wrote the code on the fly - your changes are exactly according to my thoughts :) Commented Dec 29, 2015 at 10:44

2 Answers 2

1

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.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you for your reply. You are correct that I could lose the TOuter in my example, but that is just due to not giving a real life example - I would need the TOuter in my real life cases. As for if this is the sort of thing the C# need to spend time on, I'm not sure. It may just be my style of coding which often gets me into this kind of problem, but I would find it very handy and a very good abstraction to have. Perhaps something like: public abstract class Handler<TOuter, implicit TInner> where...?
@ChrisRidge that syntax may work, but hardly with the way the system is currently done since whatever or not that generic parameter was implicit will be lost. That's why I see it as inference on the derived type. How many generic parameters do you have? [I can't think of a case for more than 4] maybe it is a perk of what you are developing, or maybe you are missing some abstraction... Yet if it eases some particular case there maybe interest by the roslyn team. Consider optional - it is not really needed, but it eases interop and code maintenance.
In my current project I haven't got more than four myself, but these are in interfaces that are implemented many times. Having to specify all four types for each implementation is tedious and makes the code harder to understand. This is absolutely not a must-have feature, but for us it would be a great-to-have feature :)
1

Another possible way to leverage type inference in order to simplify the nested generics boilerplate needed to create these objects is by using a factory method:

// Generic class
public class Wrapper<T>
{

    public Wrapper(T wrapped)
    {
        Wrapped = wrapped;
    }

    public T Wrapped { get; set; }
}

// Factory
public class Wrapper
{
    public static Wrapper<T> Create<T>(T wrapped)
    {
        return new Wrapper<T>(wrapped);
    }
}

// Usage:
var wrappedInt = Wrapper.Create(42); // T is inferred

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.