C#编译器用于解析lambda表达式中的类型的规则是什么?

参考为什么不能将匿名方法分配给var? ,我知道C#不支持以下内容:

var func = (x,y) => Math.Log(x) + Math.Log(y); 

但是,我可以创建一个方法, Func的forms:

 public static Func Func(Func f) => f; 

然后做:

 var func = Func((x,y) => Math.Log(x) + Math.Log(y)); 

那将编译得很好。 但是,对于参数和返回值具有不同类型的lambda,事情变得奇怪。 例如,如果我有一个方法:

 public static Func Func(Func f) => f; 

我可以这样做:

 var func = Func((double x, double y) => $"{x + y}"); 

那也将编译。 所以C#编译器似乎能够推断出lambda的返回类型。 但是,以下内容将无法编译:

 var func = Func((x,y) => Math.Log(x) + Math.Log(y)); 

因为编译器似乎无法从它们在正文中使用的方式推断出xy的类型。

所以,我的问题是:关于lambda表达式类型推断的明确规则是什么; 编译器会推断出什么,不会推断出什么?

关于lambda表达式类型推断的明确规则是什么?

最终规则在规范中。 如果您想查看实现 ,可以在Roslyn源代码中轻松找到它; 我非常评价它,期待会有问题。 请注意,特别是从第110行开始的评论与您的​​问题相关; 如果您想深入了解lambda类型推断是如何工作的,请仔细研究这一点。

https://github.com/dotnet/roslyn/blob/master/src/Compilers/CSharp/Portable/Binder/Semantics/OverloadResolution/MethodTypeInference.cs

考虑自己在调试模式下编译编译器; 然后,您可以在断点处使用Dump方法来描述类型推理引擎的状态。 我使用它来促进更快速的调试,并考虑扩展算法。 (其中一些仍然在代码中,注释掉了。)

编译器会推断出什么,不会推断出什么?

这是一个过于宽泛的问题,无法明确回答。 但是关于你问题中的例子的基本行动是:

  • 类型参数的界限是从与forms参数类型匹配的普通参数推断出来的。
  • 推断或已知lambda的所有forms参数类型的Lambdas都推断出它们的返回类型
  • 重复上一步骤,直到算法未能取得进展或推断出矛盾。

我录制了一段video – 十年前现在 – 一步一步地解释所有这些,但显然已经不再是在MSDN上了。 我很烦恼。

我同意现在已经放弃的答案..只需要阅读指示的参考资料,以了解c#的匿名类型推断的确切规则,而且我也承认没有必要的知识来说明多少这个问题很广泛,但我相信并且我相信谁说这是一个非常广泛的问题。

但是,我原本可以预料到,规则是明确的,那么编译器应该可行,因为某些类型的推论的规则是合乎逻辑的而不是任意的。

在您的示例中,无法执行最后一次计算,因为每个参数的类型可能不是“双”类型或其他一些兼容类型,这是正确的。 但相反,如果你已经编写了你的​​lambda表达式,以便返回类型函数的推断不可能出错,那么,我希望它能工作,相反它不起作用。

例如,以下代码,我希望它可以工作但不会:

 public static Func Func(Func f) => f; var func = Func((x, y) => string.Format("{0},{1}", x.ToString(), y.ToString())); 

或者,这可以工作,而不是:

 double x1 = 0, y1 = 0; var func = Func((x, y) => Math.Log(double.TryParse(x.ToString(),x1)) + Math.Log(double.TryParse(y.ToString(), y1))); 

因此,在我看来,编译器完全避免完全做出各种推断。 当它是安全的,那么它执行计算,否则不执行。

我希望我接近你的问题,或者至少给出了一个有用的指示或跟踪..

您必须提供类型所有参数和结果,或者:

  • 明确地,通过在通用尖括号( )之间或在lambda参数列表中声明它(例如: (int a, int b) => { /*...*/ } )。
  • 隐式地,编译器可以推断出结果类型的位置,与推断var类型的方式相同。

  • 这个编译是因为这里提供的类型是double

     public static Func Func(Func f) => f; var func = Func((x,y) => Math.Log(x) + Math.Log(y)); 
  • 这也会编译,因为您已将T1T2提供为double ,并且可以将T3推断为string

     public static Func Func(Func f) => f; var func = Func((double x, double y) => $"{x + y}"); 
  • 但是,由于T1T2的类型未知,因此无法编译。 编译器不能使用它们的类型。

     public static Func Func(Func f) => f; var func = Func((x,y) => Math.Log(x) + Math.Log(y));