Reputation: 43254
With reference to Why can't an anonymous method be assigned to var?, I understand that the following is not supported in C#:
var func = (x,y) => Math.Log(x) + Math.Log(y);
However, I can create a method, Func
of the form:
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. 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. 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.
So, my question: what are the definitive rules on type inference of lambda expressions; what will the compiler infer and what won't it?
Upvotes: 3
Views: 433
Reputation: 2128
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..
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. 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.
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..
Upvotes: 1
Reputation: 660138
what are the definitive rules on type inference of lambda expressions;
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; 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; study this carefully if you want a deep understanding of how lambda type inference works.
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. 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:
I recorded a video -- ten years ago now -- explaining all this step by step but apparently it is no longer on MSDN. I am vexed.
Upvotes: 6
Reputation: 14477
You must provide the type all parameters and result, either :
<T>
) or within the lambda argument list(ex: (int a, int b) => { /*...*/ }
).var
is inferred.This compiles because the type are provided here, be 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
.
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. 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));
Upvotes: 0