为什么编译器在这种重载决策情况下不能告诉更好的转换目标?(协方差)

Jep*_*sen 16 c# delegates overloading covariance overload-resolution

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

void Method(Func<string> f)
{
}
void Method(Func<object> f)
{
}
void Call()
{
    Method(() => { throw new NotSupportedException(); });
}
Run Code Online (Sandbox Code Playgroud)

这给出了编译时错误CS0121,以下方法或属性之间的调用是不明确的:后跟我的两个Method函数成员(重载).

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

由于.NET 4和C#4(2010),通用委托类型Func<out TResult>协变TResult,并且由于该原因的隐式转换从存在Func<string>Func<object>同时明确的隐式转换可以从存在Func<object>Func<string>.那么它会产生Func<string>更好的转换目标,而重载分辨率应该选择第一个过载?

我的问题很简单:我在这里错过了C#规范的哪一部分?


增加:这很好用:

void Call()
{
    Method(null); // OK!
}
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 17

我的问题很简单:我在这里错过了C#规范的哪一部分?

摘要:

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

细节:

C#5规范说更好的规则是:

  • 如果表达式具有类型,则选择从该类型到候选参数类型的更好转换.

  • 如果表达式没有类型且不是lambda,请选择转换为更好的类型.

  • 如果表达式是lambda,那么首先考虑哪个参数类型更好; 如果两者都不是更好并且委托类型具有相同的参数列表,则考虑lambda的推断返回类型与委托的返回类型之间的关系.

所以预期的行为是:首先编译器应该检查一个参数类型是否明显优于另一个参数类型,无论参数是否具有类型.如果这不能解决问题并且参数是lambda,那么检查转换为参数'委托类型'返回类型的推断返回类型哪个更好.

实现中的错误是实现不这样做.相反,在参数是lambda的情况下,它完全跳过类型更好性检查并直接进入推断的返回类型更好性检查,然后由于没有推断的返回类型而失败.

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

我注意到在我在C#4中添加委托差异之前,这个bug基本上是不可能的; 在C#3中,两个不同的委托类型不可能或多或少具体,因此唯一可以应用的规则是lambda规则.由于C#3中没有可以揭示错误的测试,因此很容易编写.我的坏,对不起.

我还注意到,当你开始将表达式树类型投入混合时,分析变得更加复杂.即使Func<string>比更好Func<object>,Expression<Func<string>>也不可兑换Expression<Func<object>>!如果关于lambda是否要转移到表达式树或委托,那么更好的算法是不可知的,但它在某种程度上不是.那些案件变得复杂,我不想在这里强调这一点.

这个小错误是实现规范实际所说内容的重要性的对象课程,而不是您认为的内容.如果我在C#3中更加小心以确保代码与规范匹配,则代码在"null"情况下会失败,之后很明显C#3规范是错误的.并且实现在类型检查之前进行lambda检查,这是一个定时炸弹等待C#4转过来然后突然变成不正确的代码.无论如何都应首先进行类型检查.

  • 嗨,埃里克,谢谢你的回答!我刚刚尝试使用Roslyn编译它,你最终在诊断中得到了相同的模糊调用消息,所以我猜它没有进入. (2认同)
  • @JeppeStigNielsen:好啊,它已经在规范中修复了.我不在我的工作机器上,我在这里有一个较旧的规范副本.该规范的早期版本没有该语言. (2认同)