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?
ConsumeFunc2b
shows that forFunc<?, T>
the return typeT
can be resolved fromTakeAndGiveInt
. But when?
is alsoT
as is the case withFunc<T, T>
inConsumeFunc2c
, the compiler seems to forget the parameterT
is the same as the already inferredT
s. Quite unlike theConsumeFunc1Func2
success. – Ark-kun