Pressacco
Pressacco

Reputation: 2875

How does compiler infer the delegate type from LAMBDA expression?

SAMPLE CODE

int id = 123;

ThreadPool.QueueUserWorkItem(state => ThreadEntryPoint((int)state), id);

public void ThreadEntryPoint(int uniqueId)
{
   Console.WriteLine("uniqueId=" + uniqueId);
}

QUESTION

From the provided LAMBDA expression, how does the compiler know that it needs to create an instance of QueueUserWorkItem(WaitCallback, Object)?

More specifically: I understand that it is inferring the delegate type. What I don't understand is what decision tree (from a high level) is it going through to select the correct delegate type to instantiate?

REFERENCES

Upvotes: 3

Views: 529

Answers (1)

Peter Duniho
Peter Duniho

Reputation: 70671

How does compiler infer the delegate type from LAMBDA expression?

Fundamentally, it doesn't.

The compiler infers the delegate type from the available overloads in the method group named QueueUserWorkItem. There are only two overloads, and only one with two parameters, and both overloads use the delegate type WaitCallback.

Thus, the delegate type has to be WaitCallback. With this determined, the compiler can then compile the lambda expression as an anonymous method and instantiate as the parameter to the QueueUserWorkItem() method call the necessary delegate object to call that anonymous method.

In a more complicated scenario, the compiler would have to perform some analysis to determine the "best" match for the overload, and that analysis may involve the lambda expression to the extent that it can eliminate overload possibilities based on the lambda expression.

But at no point does the compiler start with the lambda expression and go directly to the delegate type. For a lambda expression to be converted to a delegate instance, there needs to be some other context for the lambda expression that determines the needed delegate type, such as assignment to a typed variable, an explicit cast, or (as in this case) a method overload where the parameter the lambda expression is used for has a specific delegate type compatible with the lambda expression.

Note that there is still type inference for generic methods, in which the lambda is used to infer type parameters. Eric Lippert's comment explains this clearly:

There are some instances where the compiler must infer the constructed delegate type from the lambda. For example, if we have M<A, R>(Func<A, R> f) and M((string x) => x.Length) then the compiler will infer Func<string, something> first, from the lambda parameter, and then Func<string, int> from the body of the lambda.

My point here is that the compiler won't infer Func<T, TResult> from scratch. As Eric points out, the compiler does use the lambda to infer the type parameters, but the compiler still requires context in which the open generic type is known.

For more details, you should read the C# specification. It will detail how the overload resolution is performed and the rules that govern that and matching lambda expressions to types.

Additional reading:
.Net lambda expression— where did this parameter come from?
Why can't an anonymous method be assigned to var?
Why can't the compiler tell the better conversion target in this overload resolution case? (covariance)
Convert this delegate to an anonymous method or lambda
Not coincidentally, the first three include excellent discussions of the topic and related issues written by Eric Lippert, who used to work on the Visual Studio C# compiler and language design teams.

Upvotes: 4

Related Questions