Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# 3.0 generic type inference - passing a delegate as a function parameter

Tags:

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 delegate for the same method.

Here is an example:

class Test {     static void foo(int x) { }     static void bar<T>(Action<T> f) { }      static void test()     {         Action<int> f = foo; // I can do this         bar(f); // and then do this         bar(foo); // but this does not work     }    } 

I would have thought that I would be able to pass foo to bar and have the compiler infer the type of Action<T> from the signature of the function being passed but this does not work. However I can create an Action<int> from foo without casting so is there a legitimate reason that the compiler could not also do the same thing via type inference?

like image 649
Andrew Hare Avatar asked Jan 02 '09 20:01

Andrew Hare


2 Answers

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.

  • In the assignment statement, the compiler can tell clearly which foo you're talking about, since the int type is specified.
  • In the barz(foo) statement, the compiler can tell which foo you're talking about, since the int type is specified.
  • In the bar(foo) statement, it could be any foo with a single parameter - so the compiler gives up.

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.

like image 151
Amy B Avatar answered Oct 16 '22 18:10

Amy B


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>.

like image 38
configurator Avatar answered Oct 16 '22 16:10

configurator