了解有关重载解析的C#语言规范显然很困难,现在我想知道为什么这个简单的案例失败了:

void Method(Func<string> f)
{
}
void Method(Func<object> f)
{
}
void Call()
{
    Method(() => { throw new NotSupportedException(); });
}

这会产生编译时错误CS0121,以下方法或属性之间的调用不明确:其后是我的两个Method函数成员(重载)。

我本来希望的是Func<string>Func<object>是更好的转换目标,然后应该使用第一个重载。

从.NET 4和C#4(2010)开始,通用委托(delegate)类型Func<out TResult>TResult中为协变量,因此从隐含转换从Func<string>转换为Func<object>,但显然不存在从Func<object>Func<string>的隐式转换。因此,它将使Func<string>成为更好的转换目标,并且重载分辨率应该选择第一个重载?

我的问题很简单:我在这里缺少C#Spec的哪一部分?

添加:可以正常工作:
void Call()
{
    Method(null); // OK!
}

最佳答案



概要:

  • 您在实现中发现了一个较小的已知错误。
  • 出于向后兼容的原因,将保留该错误。
  • C#3规范包含有关如何处理“空”情况的错误。它已在C#4规范中修复。
  • 您可以使用无法推断返回类型的任何lambda重现 buggy 行为。例如:Method(() => null);

  • 细节:

    C#5规范说,更好的规则是:
  • 如果表达式具有类型,则从该类型到候选参数类型选择更好的转换。
  • 如果表达式没有类型并且不是lambda,则选择转换为更好的类型。
  • 如果表达式是lambda,则首先考虑哪种参数类型更好;如果两者都不是更好,并且委托(delegate)类型具有相同的参数列表,则请考虑lambda的推断返回类型与委托(delegate)的返回类型之间的关系。

  • 因此,预期的行为是:首先,编译器应检查以查看一种参数类型是否明显优于另一种参数类型,而不管参数是否具有类型。如果那不能解决问题,并且参数是lambda,则检​​查以哪种推断的返回类型转换为参数的委托(delegate)类型的返回类型更好。

    实现中的错误是实现没有做到这一点。相反,在参数是lambda的情况下,它会完全跳过类型更好性检查,而直接进入推断的返回类型更好性检查,由于没有推断的返回类型,该检查将失败。

    我的意图是为罗斯林解决此问题。但是,当我去实现它时,我们发现进行修复导致一些实际代码停止编译。 (我不记得实际的代码是什么,并且我不再有权访问存在兼容性问题的数据库。)因此,我们决定维护现有的小错误。

    我注意到在我在C#4中添加委托(delegate)方差之前,该错误基本上是不可能的。在C#3中,两个不同的委托(delegate)类型不可能或多或少地特定,因此唯一适用的规则是lambda规则。由于C#3中没有测试可以揭示该错误,因此编写起来很容易。我不好,对不起。

    我还注意到,当您开始将表达式树类型投入混合时,分析会变得更加复杂。即使Func<string>Func<object>更好,但Expression<Func<string>>不能转换为Expression<Func<object>>!如果更好的算法对于lambda是传递到表达式树还是委托(delegate)是不可知的,那将是很好的选择,但是在某些方面却不是。这些情况变得复杂,我不想在这里努力。

    这个小错误是一个对象类(class),说明了实现规范的实际含义,而不是您认为的内容。如果我在C#3中更加谨慎以确保代码与规范匹配,那么在“空”情况下代码将失败,并且之前将清楚C#3规范是错误的。该实现会在类型检查之前执行lambda检查,这是一个定时炸弹,当C#4转来转去时突然变成错误的代码。无论如何,应该先进行类型检查。

    关于c# - 在这种重载解决方案的情况下,编译器为什么不能说出更好的转换目标? (协方差),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/20398705/

    10-09 00:21
    查看更多