Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is C# unable to infer the generic type argument type from a non-generic static method's signature?

I have conducted the following inference tests:

static class InferenceTest {
    static void TakeInt(int a) { }
    static int GiveInt() { return 0; }
    static int TakeAndGiveInt(int a) { return 0; }

    static void ConsumeAction1<T>(Action<T> a) { }
    static void ConsumeFunc1<T>(Func<T> f) { }
    static void ConsumeFunc2a<T1, T2>(Func<T1, T2> f) { }
    static void ConsumeFunc2b<T>(Func<int, T> f) { }
    static void ConsumeFunc2c<T>(Func<T, T> f) { }
    static void ConsumeFunc1Func2<T1, T2>(Func<T1> f1, Func<T1, T2> f2) { }

    static void Main() {
        ConsumeAction1(TakeInt);        //error
        ConsumeFunc1(GiveInt);          //ok
        ConsumeFunc2a(TakeAndGiveInt);  //error
        ConsumeFunc2b(TakeAndGiveInt);  //ok
        ConsumeFunc2c(TakeAndGiveInt);  //error
        ConsumeFunc1Func2(GiveInt, TakeAndGiveInt); //ok
    }
}

The results seem to suggest that the C# compiler is unable to infer the generic type arguments for the delegate function parameters from a non-generic method group.

What puzzles me the most is that C# is can infer the type arguments for Func<T1, T2> from the method return values in ConsumeFunc1Func2, but is unable to infer the types for Func<T, T> in ConsumeFunc2c.

This question is similar to the T of Func<S, T> is inferred from output of lambda expression only when S and T are different? question, but instead of lambdas with unknown parameter types we have non-generic method groups.

The Why can't C# infer type from this seemingly simple, obvious case question sort of answers the questions "Why are non-ambiguous non-generic methods not enough for inference?" and "Why is there a difference between the argument types and the return value type for inference?".

Questions:

Why can the C# compiler infer the type of Func<T> using the type of the return value, but fails to see the success in the Func<T, T> case?

Why can the C# compiler infer the T1 type argument for Func<T1, T2> from the Func<T1> in ConsumeFunc1Func2, but cannot infer the T type argument for Func<T, T> from itself in ConsumeFunc2c which seems to be easier?

like image 996
Ark-kun Avatar asked Dec 18 '13 03:12

Ark-kun


1 Answers

In general, a method name will not uniquely identify a unique type Action<T> to which the method group could be assigned. For example, even if there's only one overload of Fred and it takes a single Cat argument, that overload could be assigned not just to an Action<Cat>, but also to some other types like Action<Mammal>, Action<Animal>, or Action<Object>. While there are some cases where one type substitution would be in every way superior to any alternative, that is not always the case. It's cleaner to define the language to require that the type of delegate be specified, than to have the compiler try to "guess", especially since having the compiler guess would mean that many things which shouldn't be breaking changes, would be (e.g. adding a method overload may render ambiguous a type inference which used to work).

like image 179
supercat Avatar answered Sep 20 '22 17:09

supercat