简体   繁体   English

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

[英]What are the rules the C# compiler uses to resolve types in a lambda expression?

With reference to Why can't an anonymous method be assigned to var? 参考为什么不能将匿名方法分配给var? , I understand that the following is not supported in C#: ,我知道C#不支持以下内容:

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

However, I can create a method, Func of the form: 但是,我可以创建一个方法, Func的形式:

public static Func<T,T,T> Func<T>(Func<T,T,T> f) => f;

and then do: 然后做:

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

that will compile just fine. 那将编译得很好。 However, for lambdas with different types for the parameters and return value, things get odd. 但是,对于参数和返回值具有不同类型的lambda,事情变得奇怪。 For example, if I have a method: 例如,如果我有一个方法:

public static Func<T1,T2,T3> Func<T1,T2,T3>(Func<T1,T2,T3> f) => f;

I can for example do: 我可以这样做:

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

and that too will compile. 那也将编译。 So the C# compiler seems to be able to infer the return type for a lambda. 所以C#编译器似乎能够推断出lambda的返回类型。 But, the following won't compile: 但是,以下内容将无法编译:

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

as the compiler doesn't seem able to infer the types of x and y from the way they are used in the body. 因为编译器似乎无法从它们在体内使用的方式推断出xy的类型。

So, my question: what are the definitive rules on type inference of lambda expressions; 所以,我的问题是:关于lambda表达式类型推断的明确规则是什么; what will the compiler infer and what won't it? 编译器将推断出什么,不会推断出什么?

what are the definitive rules on type inference of lambda expressions; 关于lambda表达式类型推断的明确规则是什么?

The definitive rules are in the specification. 最终规则在规范中。 If you want to look at the implementation , you can find it easily enough in the Roslyn sources; 如果您想查看实现 ,可以在Roslyn源代码中轻松找到它; I commented it pretty heavily, anticipating that there would be questions. 我非常评价它,期待会有问题。 Note that in particular the comment starting around line 110 is relevant to your question; 请注意,特别是从第110行开始的评论与您的​​问题相关; study this carefully if you want a deep understanding of how lambda type inference works. 如果您想深入了解lambda类型推断是如何工作的,请仔细研究这一点。

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

Consider compiling the compiler yourself in debug mode; 考虑在调试模式下自己编译编译器; you can then use the Dump method at breakpoints to describe the state of the type inference engine as it goes. 然后,您可以在断点处使用Dump方法来描述类型推理引擎的状态。 I used this to facilitate more rapid debugging, and when considering extensions to the algorithm. 我使用它来促进更快速的调试,并考虑扩展算法。 (Some of which are still in the code, commented out.) (其中一些仍然在代码中,注释掉了。)

what will the compiler infer and what won't it? 编译器将推断出什么,不会推断出什么?

That is far too broad a question to answer definitively . 这是一个过于宽泛的问题,无法明确回答。 But the basic action with respect to the examples in your question is: 但是关于你问题中的例子的基本动作是:

  • Bounds on type parameters are inferred from ordinary arguments matched with formal parameter types. 类型参数的界限是从与形式参数类型匹配的普通参数推断出来的。
  • Lambdas where all formal parameter types of the lambda are inferred or known have their return types inferred 推断或已知lambda的所有形式参数类型的Lambdas都推断出它们的返回类型
  • The previous step is repeated until the algorithm fails to make progress or deduces a contradiction. 重复上一步骤,直到算法无法取得进展或推断出矛盾。

I recorded a video -- ten years ago now -- explaining all this step by step but apparently it is no longer on MSDN. 我录制了一段视频 - 十年前现在 - 一步一步地解释所有这些,但显然已经不再是在MSDN上了。 I am vexed. 我很烦。

I agree with answers that have been given up now.. there is just needs to read the indicated reference for to know the exact rules of c#'s anonymous types's inference, and also I do admit to don't have the necessary knowledge for say how much broad is this question but I believe and I trust with who states that is a very broad question.. 我同意现在已经放弃的答案..只需要阅读指示的参考资料,以了解c#的匿名类型推断的确切规则,而且我也承认没有必要的知识来说明多少这个问题很广泛,但我相信并且我相信谁是一个非常广泛的问题。

However I would have expected that, where the rules were unequivocal, then compiler should work, because the rules of some types of inferences are logical and not arbitrary. 但是,如果规则是明确的,我会期望编译器应该工作,因为某些类型的推论的规则是合乎逻辑的而不是任意的。

In your example, the last compute cannot be carried out, because the type of each parameter could be everything else than a "double" type or some other compatible type, and this is right. 在您的示例中,无法执行最后一次计算,因为每个参数的类型可能是除“double”类型或其他兼容类型之外的所有内容,这是正确的。 But instead, if you had composed your lambda expression so that it were impossible to go wrong the inference of return type function, then, I would expected that it works, instead it doesn't work. 但相反,如果你已经编写了你的​​lambda表达式,以便返回类型函数的推断不可能出错,那么,我希望它能够工作,相反它不起作用。

For example, the following code, I expect it works but doesn't: 例如,以下代码,我希望它可以工作,但不会:

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

or again, this sould work, instead doesn't: 或者,这可以工作,而不是:

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

So, it seems to me quite clear that compiler avoid totally to make every sort of inference. 因此,在我看来,编译器完全避免完全做出各种推断。 When it is safe, then it carries out compute, else not. 当它是安全的,那么它执行计算,否则不执行。

I hope I approached your question, or at least to have given a useful indication or trace.. 我希望我接近你的问题,或者至少给出了一个有用的指示或追踪......

You must provide the type all parameters and result, either : 您必须提供类型所有参数和结果,或者:

  • Explicitly, by declaring it between the generic angle brackets( <T> ) or within the lambda argument list(ex: (int a, int b) => { /*...*/ } ). 明确地,通过在通用尖括号( <T> )之间或在lambda参数列表中声明它(例如: (int a, int b) => { /*...*/ } )。
  • Implicitly, where the compiler can infer the resulting type, the same way the type of var is inferred. 隐式地,编译器可以推断出结果类型的方式,与推断var类型的方式相同。

  • This compiles because the type are provided here, be double . 这个编译是因为这里提供的类型是double

     public static Func<T,T,T> Func<T>(Func<T,T,T> f) => f; var func = Func<double>((x,y) => Math.Log(x) + Math.Log(y)); 
  • This also compiles because you've provided T1 and T2 as double , and T3 can be inferred as a string . 这也会编译,因为您已将T1T2提供为double ,并且可以将T3推断为string

     public static Func<T1,T2,T3> Func<T1,T2,T3>(Func<T1,T2,T3> f) => f; var func = Func((double x, double y) => $"{x + y}"); 
  • However, this does not compile because the type of T1 and T2 is unknown. 但是,由于T1T2的类型未知,因此无法编译。 The compiler cannot their types from the way they are being used. 编译器不能使用它们的类型。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM