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));
因为编译器似乎无法从它们在正文中使用的方式推断出x
和y
的类型。
所以,我的问题是:关于lambda表达式类型推断的明确规则是什么; 编译器会推断出什么,不会推断出什么?
关于lambda表达式类型推断的明确规则是什么?
最终规则在规范中。 如果您想查看实现 ,可以在Roslyn源代码中轻松找到它; 我非常评价它,期待会有问题。 请注意,特别是从第110行开始的评论与您的问题相关; 如果您想深入了解lambda类型推断是如何工作的,请仔细研究这一点。
考虑自己在调试模式下编译编译器; 然后,您可以在断点处使用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)); -
这也会编译,因为您已将
T1
和T2
提供为double
,并且可以将T3
推断为string
。public static Func
Func (Func f) => f; var func = Func((double x, double y) => $"{x + y}"); -
但是,由于
T1
和T2
的类型未知,因此无法编译。 编译器不能使用它们的类型。public static Func
Func (Func f) => f; var func = Func((x,y) => Math.Log(x) + Math.Log(y));