I am wondering why the C# 3.0 compiler is unable to infer the type of a method when it is passed as a parameter to a generic function when it can implicitly create a delegat
Just for completeness, this is not specific to C#: The same VB.NET code fails similarly:
Imports System
Module Test
Sub foo(ByVal x As integer)
End Sub
Sub bar(Of T)(ByVal f As Action(Of T))
End Sub
Sub Main()
Dim f As Action(Of integer) = AddressOf foo ' I can do this
bar(f) ' and then do this
bar(AddressOf foo) ' but this does not work
End Sub
End Module
error BC32050: Type parameter 'T' for 'Public Sub bar(Of T)(f As System.Action(Of T))' cannot be inferred.
Maybe this will make it clearer:
public class SomeClass
{
static void foo(int x) { }
static void foo(string s) { }
static void bar<T>(Action<T> f){}
static void barz(Action<int> f) { }
static void test()
{
Action<int> f = foo;
bar(f);
barz(foo);
bar(foo);
//these help the compiler to know which types to use
bar<int>(foo);
bar( (int i) => foo(i));
}
}
foo is not an action - foo is a method group.
Edit: I've added two (more) ways to help the compiler figure out the type (ie - how to skip the inference steps).
From my reading of the article in JSkeet's answer, the decision to not infer the type seems to be based on a mutual infering scenario, such as
static void foo<T>(T x) { }
static void bar<T>(Action<T> f) { }
static void test()
{
bar(foo); //wut's T?
}
Since the general problem was unsolve-able, they choose to left specific problems where a solution exists as unsolved.
As a consequence of this decision, you won't be adding a overload for a method and getting a whole lot of type confusion from all the callers that are used to a single member method group. I guess that's a good thing.
The reasoning is that if the type ever expands there should be no possibility of failure. i.e., if a method foo(string) is added to the type, it should never matter to existing code - as long as the contents of existing methods don't change.
For that reason, even when there is only one method foo, a reference to foo (known as a method group) cannot be cast to a non-type-specific delegate, such as Action<T>
but only to a type-specific delegate such as Action<int>
.
Keep in mind that the assignment
Action<int> f = foo;
already has lots of syntactic sugar. The compiler actually generates code for this statement:
Action<int> f = new Action<int>(foo);
The corresponding method call compiles without problem:
bar(new Action<int>(foo));
Fwiw, so does helping the compiler to deduce the type argument:
bar<int>(foo);
So it boils down to the question, why the sugar in the assignment statement but not in the method call? I'd have to guess that it's because the sugar is unambiguous in the assignment, there is only one possible substitution. But in the case of method calls, the compiler writers already had to deal with the overload resolution problem. The rules of which are quite elaborate. They probably just didn't get around to it.
That is slightly odd, yes. The C# 3.0 spec for type inference is hard to read and has mistakes in it, but it looks like it should work. In the first phase (section 7.4.2.1) I believe there's a mistake - it shouldn't mention method groups in the first bullet (as they're not covered by explicit parameter type inference (7.4.2.7) - which means it should use output type inference (7.4.2.6). That looks like it should work - but obviously it doesn't :(
I know that MS is looking to improve the spec for type inference, so it might become a little clearer. I also know that regardless of the difficulty of reading it, there are restrictions on method groups and type inference - restrictions which could be special-cased when the method group is only actually a single method, admittedly.
Eric Lippert has a blog entry on return type inference not working with method groups which is similar to this case - but here we're not interested in the return type, only on the parameter type. It's possible that other posts in his type inference series may help though.