Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Named arguments and generic type inference in C# 4.0

I had been programming under the assumption that, when calling a method in C# 4.0, supplying names for your arguments would not affect the outcome unless in doing so you were "skipping" one or more optional parameters.

So I was a bit surprised to discover the following behavior:

Given a method which takes a Func<T>, executes it and returns the result:

public static T F<T>(Func<T> f) {     return f(); } 

And another method from which the above method is visible:

static void Main() {     string s; 

calling F (without named arguments) compiles without any issues:

    s = F<string>(() => "hello world"); // with explicit type argument <string>     s = F(() => "hello world"); // with type inference 

And when using a named argument...

    s = F<string>(f: () => "hello world"); 

... the above line of code using the explicit type argument still compiles without issues. And maybe not too surprisingly, if you have ReSharper installed it will suggest that the "Type argument specification is redundant".

However, when removing the type argument...

    s = F(f: () => "hello world"); 

the C# compiler will report this error:

The type arguments for method 'Program.F(System.Func)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

Is there a logical explanation for this interaction between named arguments and type inference?

Is this behavior documented somewhere in the language specification?

I understand that it is not necessary at all for me to name the argument. However, I discovered this behavior in a much more complex scenario where I thought it might make sense to name the arguments in my method call for internal documentation purposes. I am not asking how to work around this issue. I am trying to understand some of the finer points of the language.

To make things more interesting I discovered that the following all compiles without issues:

    Func<string> func = () => "hello world";     s = F<string>(func);     s = F(func);     s = F<string>(f: func);     s = F(f: func); } 

By the way I have observed the same behavior with non-static methods. I just chose to use static methods to make the example here a bit shorter.

like image 874
BirgerH Avatar asked Jul 01 '11 01:07

BirgerH


People also ask

What is generic type arguments?

The generic argument list is a comma-separated list of type arguments. A type argument is the name of an actual concrete type that replaces a corresponding type parameter in the generic parameter clause of a generic type. The result is a specialized version of that generic type.

How to define a generic method in C#?

A generic type is declared by specifying a type parameter in an angle brackets after a type name, e.g. TypeName<T> where T is a type parameter.

What is type parameter in C#?

In a generic type or method definition, a type parameter is a placeholder for a specific type that a client specifies when they create an instance of the generic type.

Does C# support type inference?

In C#, the var keyword tells the compiler to use the type inference to determine the type of a variable. Type inference is heavily used in LINQ queries so any type can be stored in the variable.


2 Answers

Inference is not something that will work at many nested levels in compilation. It is kind of a guess based on arguments supplied. I feel the compiler writers did not consider inferring logic along with named parameter. If you consider abstract syntax tree, Even though the logic is same, but both F(()=>"xyz") And F(f:()=>"xyz") Are different abstract syntax trees from compiler's perspective.

I feel it's just a rule missed by compiler designer where even the compiler itself is a program with huge set of rules. One rule matches first case but no rule matches second one. It may be conceptually right but compiler is just a program and all rules are human coded.

Ok, I guess as others have determined, its a bug and should be reported to Microsoft !!

like image 173
Akash Kava Avatar answered Oct 11 '22 16:10

Akash Kava


Just want to let you know this is a bug specific to C# (and @leppie I have confirmed it does fail with the standard csc.exe, not even in Visual Studio). Redundantly specifying a named argument shouldn't cause a change in behaviour at all.

The bug has been reported at Microsoft Connect.

The equivalent VB works fine (because it does compile I added a little bit to confirm the runtime behaviour is as expected):

Imports System  Module Test   Function F(Of T)(ByVal fn As Func(Of T)) As T     Return fn()   End Function    Function Inc(ByRef i as Integer) As String     i += 1     Return i.ToString   End Function    Sub Main()     Dim s As String     s = F(Of String)(Function()"Hello World.") console.writeline(s)     s = F(Function()"Hello, World!") console.writeline(s)     s = F(Of String)(fn:=Function()"hello world.") console.writeline(s)     s = F(fn:=Function()"hello world") console.writeline(s)     Dim i As Integer     Dim func As Func(Of String) = Function()"hello world " & Inc(i)     s = F(Of string)(func) console.writeline(s)     s = F(func) console.writeline(s)     s = F(Of string)(fn:=func) console.writeline(s)     s = F(fn:=func) console.writeline(s)   End Sub End Module 

Output:

Hello World. Hello, World! hello world. hello world hello world 1 hello world 2 hello world 3 hello world 4 
like image 30
Mark Hurd Avatar answered Oct 11 '22 17:10

Mark Hurd