且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

为什么 Func T与 Func<IEnumerable<T>> 有歧义?

更新时间:2022-05-05 15:40:04

好的,事情来了.

简短版本:

  • 奇怪的是,歧义错误是正确的.
  • 在正确的歧义错误之后,C# 4 编译器也会产生虚假错误.这似乎是编译器中的一个错误.

长版:

我们有一个重载解析问题.重载分辨率非常明确.

We have an overload resolution problem. Overload resolution is extremely well specified.

第一步:确定候选集.这很容易.候选对象是 Foo(Func>)Foo(Func).

Step one: determine the candidate set. That's easy. The candidates are Foo(Func<IEnumerable<String>>) and Foo(Func<String>).

第二步:确定候选集的哪些成员适用.适用成员的每个参数都可以转换为每个参数类型.

Step two: determine which members of the candidate set are applicable. An applicable member has every argument convertible to every parameter type.

Foo(Func>) 是否适用?那么,X 是否可以转换为 Func?

Is Foo(Func<IEnumerable<String>>) applicable? Well, is X convertible to Func<IEnumerable<String>?

我们参考规范的第 6.6 节.这部分规范就是我们语言设计者所说的真的很奇怪".基本上,它表示可以存在转换,但使用该转换是错误的.(我们遇到这种奇怪的情况是有充分的理由的,主要是为了避免未来的重大变化和避免鸡与蛋"的情况,但在你的情况下,我们因此得到了一些不幸的行为.)

We consult section 6.6 of the spec. This part of the specification is what we language designers call "really weird". Basically, it says that a conversion can exist, but using that conversion is an error. (There are good reasons why we have this bizarre situation, mostly having to do with avoiding future breaking changes and avoiding "chicken and egg" situations, but in your case we are getting somewhat unfortunate behaviour as a result.)

基本上,这里的规则是,如果 X() 形式的调用的重载解析成功,则从 X 到无参数委托类型的转换存在.显然,这样的调用成功,因此存在转换.实际上使用这种转换是错误的,因为返回类型不匹配,但是重载解析总是忽略返回类型.

Basically, the rule here is that a conversion from X to a delegate type with no parameters exists if overload resolution on a call of the form X() would succeed. Clearly such a call would succeed, and therefore a conversion exists. Actually using that conversion is an error because the return types don't match, but overload resolution always ignores return types.

因此,存在从 XFunc 的转换,因此该重载是适用的候选者.

So, a conversion exists from X to Func<IEnumerable<String>, and therefore that overload is an applicable candidate.

显然出于同样的原因,另一个重载也是一个适用的候选对象.

Obviously for the same reason the other overload is also an applicable candidate.

第三步:我们现在有两个适用的候选人.哪个更好"?

Step Three: We now have two applicable candidates. Which one is "better"?

更好"的是具有更具体类型的那个.如果您有两个适用的候选者,M(Animal)M(Giraffe),我们选择 Giraffe 版本,因为 Giraffe 比 Animal 更具体.我们知道长颈鹿更具体,因为每只长颈鹿都是动物,但并非每只动物都是长颈鹿.

The one that is "better" is the one with the more specific type. If you have two applicable candidates, M(Animal) and M(Giraffe) we choose the Giraffe version because a Giraffe is more specific than an Animal. We know that Giraffe is more specific because every Giraffe is an Animal, but not every Animal is a Giraffe.

但在您的情况下,两种类型都不比另一种更具体.两种 Func 类型之间没有转换.

But in your case neither type is more specific than the other. There is no conversion between the two Func types.

因此两者都不是更好,所以重载解析会报错.

Therefore neither is better, so overload resolution reports an error.

然后,C# 4 编译器出现了一个错误,它的错误恢复模式无论如何都会选择一个候选者,并报告另一个错误.我不清楚为什么会这样.基本上是说错误恢复是选择 IEnumerable 重载,然后注意到方法组转换产生了站不住脚的结果;即,该字符串与 IEnumerable 不兼容.

The C# 4 compiler then has what appears to be a bug, where its error recovery mode picks one of the candidates anyways, and reports another error. It's not clear to me why that is happening. Basically it is saying that error recovery is choosing the IEnumerable overload, and then noting that the method group conversion produces an untenable result; namely, that string is not compatible with IEnumerable<String>.

整个情况相当不幸;如果返回类型不匹配,***说没有方法组到委托的转换.(或者,产生错误的转换总是比没有错误的转换更糟糕.)但是,我们现在坚持使用它.

The whole situation is rather unfortunate; it might have been better to say that there is no method-group-to-delegate conversion if the return types do not match. (Or, that a conversion that produces an error is always worse than a conversion that does not.) However, we're stuck with it now.

一个有趣的事实:lambdas 的转换规则考虑了返回类型.如果你说 Foo(()=>X()) 那么我们就做对了.不幸的是,lambda 和方法组具有不同的可转换规则.

An interesting fact: the conversion rules for lambdas do take into account return types. If you say Foo(()=>X()) then we do the right thing. The fact that lambdas and method groups have different convertibility rules is rather unfortunate.

总而言之,在这种情况下,编译器实际上是规范的正确实现,而这种特殊情况是一些可以说是不幸的规范选择的意外后果.

So, summing up, the compiler is actually a correct implementation of the spec in this case, and this particular scenario is an unintended consequence of some arguably unfortunate spec choices.