这个让我感到沮丧,所以我想我会在这里问,希望C#guru可以向我解释.
为什么这段代码会产生错误?
class Program
{
static void Main(string[] args)
{
Foo(X); // the error is on this line
}
static String X() { return "Test"; }
static void Foo(Func<IEnumerable<String>> x) { }
static void Foo(Func<String> x) { }
}
Run Code Online (Sandbox Code Playgroud)
有问题的错误:
Error
1
The call is ambiguous between the following methods or properties:
'ConsoleApplication1.Program.Foo(System.Func<System.Collections.Generic.IEnumerable<string>>)' and 'ConsoleApplication1.Program.Foo(System.Func<string>)'
C:\Users\mabster\AppData\Local\Temporary Projects\ConsoleApplication1\Program.cs
12
13
ConsoleApplication1
Run Code Online (Sandbox Code Playgroud)
我使用的是什么类型并不重要 - 如果在该代码中用"int"替换"String"声明,您将得到相同类型的错误.这就像编译器不能告诉之间的区别Func<T>和Func<IEnumerable<T>>.
有人可以对此有所了解吗?
Eri*_*ert 26
好的,这是交易.
简短版本:
长版:
我们有一个重载解决问题.过载分辨率非常明确.
第一步:确定候选集.这很简单.候选人是Foo(Func<IEnumerable<String>>)和Foo(Func<String>).
第二步:确定候选集的哪些成员适用.适用的成员具有可转换为每个参数类型的每个参数.
是否Foo(Func<IEnumerable<String>>)适用?好吧,可X兑换成Func<IEnumerable<String>?
我们参考规范的6.6节.规范的这一部分是我们语言设计者所说的"非常奇怪".基本上,它表示转换可以存在,但使用该转换是一个错误.(有很好的理由说明我们为什么会遇到这种奇怪的情况,主要是为了避免将来发生变化并避免"鸡蛋和鸡蛋"的情况,但在你的情况下,我们会因此而得到一些不幸的行为.)
基本上,这里的规则是从X到委托类型转换不带参数的存在,如果对形式的呼叫重载X()会成功.显然,这样的呼叫会成功,因此存在转换.实际上使用该转换是一个错误,因为返回类型不匹配,但重载解析始终忽略返回类型.
所以,转换从存在X到Func<IEnumerable<String>,因此该过载是适用的候选者.
显然,出于同样的原因,另一个过载也是适用的候选者.
第三步:我们现在有两个适用的候选人.哪一个更好"?
"更好"的是具有更具体类型的那个.如果你有两个适用的候选人,M(Animal)而M(Giraffe)我们选择了长颈鹿的版本,因为长颈鹿是比动物更具体.我们知道长颈鹿更具体,因为每只长颈鹿都是动物,但不是每只动物都是长颈鹿.
但在你的情况下,两种类型都没有比另一种更具体.两种Func类型之间没有转换.
因此两者都不是更好,因此重载决策报告错误.
然后,C#4编译器出现了一个错误,其错误恢复模式无论如何都会选择其中一个候选者,并报告另一个错误.我不清楚为什么会这样.基本上它是说错误恢复正在选择IEnumerable重载,然后注意方法组转换产生无法维持的结果; 即,该字符串不兼容IEnumerable<String>.
整个局势相当不幸; 如果返回类型不匹配,可能更好的说没有方法组到委托的转换.(或者,产生错误的转换总是比没有产生错误的转换更差.)但是,我们现在仍然坚持使用它.
一个有趣的事实:lambda的转换规则确实考虑了返回类型.如果你说Foo(()=>X())那我们做对了.lambdas和方法组具有不同的可兑换规则这一事实是相当不幸的.
总而言之,在这种情况下,编译器实际上是规范的正确实现,并且这种特殊情况是一些可能不幸的规范选择的意外结果.
您的代码需要两次"魔术",一次从命名方法组转换为委托,一次执行重载解析.
尽管您只有一个命名X的方法,但编译器规则是针对多个方案的情况构建的.
此外,由于代理不必完全匹配方法签名,因此复杂性进一步增加.最重要的是,任何给定的方法都可以转换为具有相同签名的无限数量的不同委托类型.
你的特定情况看起来很简单,但一般情况非常困难,所以语言不允许.
如果您手工完成部分工作,您将解决问题.例如
Func<string> d = X;
Foo(d);
Run Code Online (Sandbox Code Playgroud)
应该编译得很好.
| 归档时间: |
|
| 查看次数: |
2859 次 |
| 最近记录: |