为什么Func 与Func <IEnumerable >不明确?
这个让我感到沮丧,所以我想我会在这里问,希望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> x) { } static void Foo(Func x) { } }
有问题的错误:
Error 1 The call is ambiguous between the following methods or properties: 'ConsoleApplication1.Program.Foo(System.Func<System.Collections.Generic.IEnumerable>)' and 'ConsoleApplication1.Program.Foo(System.Func)' C:\Users\mabster\AppData\Local\Temporary Projects\ConsoleApplication1\Program.cs 12 13 ConsoleApplication1
我使用什么类型无关紧要 – 如果在该代码中用“int”替换“String”声明,您将得到相同类型的错误。 这就像编译器无法区分Func
和Func<IEnumerable>
之间的区别。
有人可以对此有所了解吗?
好的,这是交易。
简短版本:
- 模糊性错误,奇怪的是,是正确的。
- 在正确的模糊度错误之后,C#4编译器也会产生虚假错误。 这似乎是编译器中的一个错误。
长版:
我们有一个重载解决问题。 过载分辨率非常明确。
第一步:确定候选集。 这很简单。 候选者是Foo(Func
和Foo(Func
。
第二步:确定候选集的哪些成员适用 。 适用的成员具有可转换为每个参数类型的每个参数。
Foo(Func
适用吗? 那么, X
转换为Func
吗?
我们参考规范的第6.6节。 规范的这一部分是我们语言设计者所说的“非常奇怪”。 基本上,它表示可以存在转换,但使用该转换是一个错误 。 (有很好的理由说明为什么我们有这种奇怪的情况,主要是避免未来的变化和避免“鸡和鸡蛋”的情况,但在你的情况下,我们的行为有点不幸。)
基本上,这里的规则是,如果X()
forms的调用上的重载解析成功,则从X转换为没有参数的委托类型。 显然,这样的呼叫会成功,因此存在转换。 实际上使用该转换是一个错误,因为返回类型不匹配,但重载解析总是忽略返回类型 。
因此,存在从X
到Func
,因此该重载是适用的候选者。
显然,出于同样的原因,另一个过载也是适用的候选者。
第三步:我们现在有两个适用的候选人。 哪一个更好”?
“更好”的是具有更具体类型的那个。 如果您有两个适用的候选人, M(Animal)
和M(Giraffe)
我们选择长颈鹿版本,因为长颈鹿比动物更具体。 我们知道长颈鹿更具体,因为每只长颈鹿都是动物,但不是每只动物都是长颈鹿。
但在你的情况下,两种类型都没有比另一种更具体。 两种Func类型之间没有转换。
因此两者都不是更好,因此重载决策报告错误。
然后,C#4编译器出现了一个错误,其错误恢复模式无论如何都会选择其中一个候选者,并报告另一个错误。 我不清楚为什么会这样。 基本上它是说错误恢复是选择IEnumerable重载,然后注意方法组转换产生无法维持的结果; 即,该字符串与IEnumerable
不兼容。
整个局势相当不幸; 如果返回类型不匹配,可能更好的说没有方法组到委托的转换。 (或者,产生错误的转换总是比没有产生错误的转换更差。)但是,我们现在仍然坚持使用它。
一个有趣的事实:lambdas的转换规则确实考虑了返回类型。 如果你说Foo(()=>X())
那么我们做对了。 lambdas和方法组具有不同的可兑换规则这一事实是相当不幸的。
总而言之,在这种情况下,编译器实际上是规范的正确实现,并且这种特殊情况是一些可能不幸的规范选择的意外后果。
您的代码需要两次“魔术”,一次从命名方法组转换为委托,一次执行重载解析。
尽管您只有一个名为X
方法,但编译器规则是针对存在多个的情况而构建的。
此外,由于代表不必完全匹配方法签名,因此复杂性进一步增加。 最重要的是,任何给定的方法都可以转换为具有相同签名的无限数量的不同委托类型。
你的特定情况看起来很简单,但一般情况非常困难,所以语言不允许。
如果您手工完成部分工作,您将解决问题。 例如
Func d = X; Foo(d);
应该编译得很好。
- C#模拟VolumeMute按
- Xamarin如何从android项目打开xamarin表单页面?
- C#中IEnumerable类的foreach和for循环之间的区别
- Oracle .Net ManagedDataAccess错误:无法从程序集加载类型’OracleInternal.Common.ConfigBaseClass’
- C#获取有关当前活动窗口的信息
- 如何使用ASP.Net MVC中的Task重定向到某个操作
- Asp.Net – 在页面上没有检测到javascript? (更名为标题)
- 使用Json.Net反序列化时,在运行时将System.Type用作
- 使用LINQ在C#中使用MasterMind评分算法