在C#4.0中命名参数和genericstypes推断

我一直在编程的假设是,当在C#4.0中调用方法时,为参数提供名称不会影响结果,除非这样做是“跳过”一个或多个可选参数。

所以我有点惊讶地发现以下行为:

给定一个采用Func<T> ,执行它并返回结果:

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

另一种可以看到上述方法的方法:

 static void Main() { string s; 

调用F(没有命名参数)编译没有任何问题:

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

而当使用命名参数…

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

…使用显式types参数的上述代码行仍然编译没有问题。 也许不会太奇怪,如果你安装了ReSharper,它会提示“Type参数规范是多余的”。

但是,当删除types参数…

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

C#编译器会报告这个错误:

方法“Program.F(System.Func)”的types参数不能从用法中推断出来。 尝试明确指定types参数。

有名称参数和types推断之间的这种交互是否有合理的解释?

这种行为是否logging在语言规范的某个地方?

我明白,说出这个论点根本没有必要。 然而,我发现这种行为在一个更复杂的情况下,我认为这可能是有道理的名称在我的方法调用的参数为内部文件的目的。 我不是问如何解决这个问题。 我想了解一些这个语言的更精细的部分。

为了使事情变得更有趣,我发现以下所有的编译没有问题:

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

顺便说一下,我已经观察到与非静态方法相同的行为。 我只是select使用静态方法来缩短这个例子。

推理不是在编译中的许多嵌套级别上工作。 这是基于提供的参数的猜测。 我觉得编译器编写者并没有考虑用命名参数推理逻辑。 如果考虑抽象语法树,即使逻辑相同,但是F(()=>“xyz”)和F(f:()=>“xyz”)是从编译器的angular度来看不同的抽象语法树。

我觉得这只是编译器devise师错过的一个规则,甚至编译器本身也是一个具有大量规则的程序。 一个规则匹配第一个情况,但没有规则匹配第二个。 这在概念上是正确的,但编译器只是一个程序,所有规则都是人为编码的。

好吧,我想其他人已经确定,它的错误,应该向微软报告!

只是想让你知道这一个特定于C#的bug(和@leppie我已经确认它没有使用标准的csc.exe,即使在Visual Studio中也是如此)。 冗长地指定一个命名的参数不应该导致行为的改变。

Microsoft Connect已经报告了此错误。

等价的VB工作正常(因为它编译我添加了一点点,以确认运行时行为是预期的):

 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 

输出:

 Hello World. Hello, World! hello world. hello world hello world 1 hello world 2 hello world 3 hello world 4 

使用命名参数调用函数而不使用命名参数不相同。 在命名参数的情况下,编译器采用不同的path,因为它需要先parsing命名参数。 在你的情况下,编译器试图在parsingF中的T之前找出参数f,所以它正在请求程序员明确指出。