C#4.0中的命名参数和泛型类型推断

C#4.0中的命名参数和泛型类型推断,c#,generics,c#-4.0,compiler-errors,type-inference,C#,Generics,C# 4.0,Compiler Errors,Type Inference,我一直在编程,假设在C#4.0中调用方法时,为参数提供名称不会影响结果,除非这样做是“跳过”一个或多个可选参数 因此,我有点惊讶地发现以下行为: 给定一个方法,该方法接受一个Func,执行它并返回结果: public static T F<T>(Func<T> f) { return f(); } 调用F(无命名参数)编译时不会出现任何问题: s = F<string>(() => "hello world"); // with exp

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

因此,我有点惊讶地发现以下行为:

给定一个方法,该方法接受一个
Func
,执行它并返回结果:

public static T F<T>(Func<T> f)
{
    return f();
}
调用F(无命名参数)编译时不会出现任何问题:

    s = F<string>(() => "hello world"); // with explicit type argument <string>
    s = F(() => "hello world"); // with type inference
C#编译器将报告此错误:

无法从用法推断方法“Program.F(System.Func)”的类型参数。尝试显式指定类型参数

对于命名参数和类型推断之间的这种交互,是否有逻辑上的解释

这种行为在语言规范中有记录吗

我明白我根本没有必要说出这个论点。然而,我在一个更复杂的场景中发现了这种行为,我认为出于内部文档的目的在方法调用中命名参数是有意义的。我不是问如何解决这个问题。我正在努力理解这门语言的一些精妙之处

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

    Func<string> func = () => "hello world";
    s = F<string>(func);
    s = F(func);
    s = F<string>(f: func);
    s = F(f: func);
}
Func=()=>“你好世界”;
s=F(func);
s=F(func);
s=F(F:func);
s=F(F:func);
}

顺便说一句,我用非静态方法观察到了同样的行为。我只是选择使用静态方法来缩短这里的示例。

只是想让您知道这是一个特定于C#的bug(并且@leppie我已经确认它在标准csc.exe中确实失败,甚至在Visual Studio中也没有)。冗余地指定一个命名参数不应该导致行为的改变

该错误已在上报告

等效的VB工作正常(因为它确实可以编译,我添加了一点来确认运行时行为是否符合预期):

导入系统
模块测试
函数F(Of T)(ByVal fn作为函数(Of T))作为T
返回fn()
端函数
函数Inc(ByRef i作为整数)作为字符串
i+=1
返回i.ToString
端函数
副标题()
像线一样变暗
s=F(字符串的)(函数()“Hello World.”)
控制台写入线(s)
s=F(函数()“你好,世界!”)
控制台写入线(s)
s=F(字符串的)(fn:=Function()“hello world.”
控制台写入线(s)
s=F(fn:=Function()“hello world”)
控制台写入线(s)
作为整数的Dim i
Dim func As func(Of String)=Function()“hello world”和Inc(i)
s=F(字符串的)(func)
控制台写入线(s)
s=F(func)
控制台写入线(s)
s=F(字符串的)(fn:=func)
控制台写入线(s)
s=F(fn:=func)
控制台写入线(s)
端接头
端模块
输出:

你好,世界。
你好,世界!
你好,世界。
你好,世界
你好,世界1
你好,世界2
你好,世界3
你好,世界4

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

我觉得这只是编译器设计者遗漏的一条规则,即使编译器本身也是一个拥有大量规则的程序。一个规则匹配第一个案例,但没有规则匹配第二个案例。这在概念上可能是正确的,但编译器只是一个程序,所有规则都是人工编码的


好的,我想正如其他人所确定的,这是一个bug,应该向微软报告

使用命名参数调用函数与不使用命名参数调用函数是不同的。对于命名参数,编译器采用不同的路径,因为它需要首先解析命名参数。在您的例子中,编译器试图在解析f中的T之前计算出参数f,因此它要求程序员显式地声明。

看看这个:听起来像是一个重竖琴问题……哦,Eric!李佩特,过来解释一下!我们应该引入一个“for:eric”标记吗?作为参考,我尝试使用安装在Ubuntu上的Mono 2.6.7 C#编译器编译您的示例。它编译的问题行没有警告或错误。我想你还没有理解这个问题,你的最后一行是用vb编译的,但是你已经指定了类型(字符串),那么推理问题在哪里?@AkashKava:AFAICT我已经复制了问题中的所有C代码。当一个命名参数是一个需要类型推断的lambda表达式时,OP(我确认)C#失败。这对应于我的VB.NET行:
s=F(fn:=Function()“hello world”)
,它工作正常@Weeble的评论表明这是微软的错误。很抱歉,我想我错过了最后一行,因为我在iPhone上看到了它,不知道还有一行,因为iPhone没有显示滚动条。我不明白你为什么把VB.Net带到这里来。如果问题与框架相关,则可以。每种语言及其编译器都是唯一的。因为它在VB.Net中工作,所以不需要在C#中工作。两者都有不同的语义。@ferosekhanj:是的,不同的编译器,但制造商相同,而且VB自VB4以来(至少)有可选参数,所以它是事实上的标准。但是,是的,如果VB.NET中的效果相同,我的贡献会更重要(就像我在发布此答案的同一天确认的那样)。它应该被视为一个错误,正如OP所述:冗余命名参数不应该破坏代码。我已经在connect now(ID:678498)上报告了这一错误。如果Microsoft确认这是一个bug,我将接受“这是一个bug”作为答案。@BirgerH您是否有指向bug报告的链接,以便我们跟踪它?我试图发布完整链接,但似乎不允许这样做。连接microsoft.com
    s = F<string>(f: () => "hello world");
    s = F(f: () => "hello world");
    Func<string> func = () => "hello world";
    s = F<string>(func);
    s = F(func);
    s = F<string>(f: func);
    s = F(f: func);
}