Extra generic parameter in generic extension metho

2020-06-03 08:02发布

问题:

I would like make an extension method for the generic class A which takes yet another generictype (in this example TC), but i guess that aint possible?

class Program
{
    static void Main(string[] args)
    {
        var a = new A<B, B>();
        a.DoIt<B>();
    }
}

static class Ext
{
    public static A<TA, TB> DoIt<TA, TB, TC>(this A<TA, TB> a)
    {
        return a;
    }
}

class A<TA, TB> { }
class B { }

回答1:

If you can accept a slight syntax change, then it would be possible.

Change it to:

var a = new A<B, B>(); 
a.Do().It<B>(); 

The trick is that the Do method is an extension method on A<TA, TB>:

public static Doer<TA, TB> Do<TA, TB>(this A<TA, TB> a)
{
    return new Doer<TA, TB>(a);
}

The trick is that this signature lets type inferincing pick up TA and TB from a so that you don't have to specify them explicitly.

The Doer class provides the generic method you need:

public class Doer<TA, TB>
{
    public void It<TC>() { }
}


回答2:

No, it's possible, but you have to give the compiler some acceptable context of what "TC" is. That third parameter, TC, isn't used anywhere else in your code, so it could be anything, therefore, the compiler complains. If you add an incoming parameter to your extension method of the type TC, however, you can accomplish a situation where the compiler can infer the actual type of TC, and then you don't even have to indicate what the types are when you call the method:

class Program
{
    static void Main(string[] args)
    {
        var a = new A<B, B>();
        string tc = "Hi!";
        a.DoIt(tc);
    }
}

static class Ext
{
    public static A<TA, TB> DoIt<TA, TB, TC>(this A<TA, TB> a, TC c)
    {
        return a;
    }
}

class A<TA, TB> { }
class B { }

But you have to give the compiler some context.

That being said, specifying generic parameters is an all-or-nothing endeavor. Either the compiler can infer the types of every generic type parameter, or it can't, and you have to tell it what all of them are.



回答3:

You're right, it's not possible. You have to either specify all type parameters (TA, TB and TC), or none of them (and leave it up to the compiler's type inference).

A couple of possibilities:

  • Turn DoIt into an instance method (although I guess you've made it an extension method on purpose)
  • Add another parameter to DoIt that somehow constrains TC, meaning type inference will work

For an example of the second one, look at Enumerable.Select: it has two type parameters, for the source and destination types, but they're both inferred from the arguments passed to Select.