Jonathan Gilbert
Jonathan Gilbert

Reputation: 3840

Where does the C# specification clarify conversion of async lambda expressions to delegates?

Microsoft's documentation states:

Any lambda expression can be converted to a delegate type. The delegate type to which a lambda expression can be converted is defined by the types of its parameters and return value. If a lambda expression doesn't return a value, it can be converted to one of the Action delegate types; otherwise, it can be converted to one of the Func delegate types.

(https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-expressions)

This documentation, though, does not explain the behavior of async lambda expressions, specifically ones lacking any return statement, being converted to delegate values.

If you are trying to convert to a Func<Task>, the conversion succeeds.

If you are trying to convert to an Action, the conversion succeeds and the anonymous method emitted for the lambda is async void.

If both Action and Func<Task> are options, then the compiler selects Func<Task>.

This all makes sense, but where is it specified? Per the above paragraph, it shouldn't work this way. The paragraph quoted above doesn't allow for async methods that don't return a value having a return type of Task. I've done some searching and haven't found anything covering this. It must be out there, though, right? :-)

Upvotes: 1

Views: 118

Answers (1)

Jon Skeet
Jon Skeet

Reputation: 1500485

That language reference isn't really the spec, although it's reasonably "spec-like".

The relevant section of the draft C# 7 spec on the Microsoft site is here, based on the ECMA standard GitHub repo with the section here.

The relevant bullets in 10.7.1 for the conditions under which "an anonymous function F is compatible with a delegate type D" are:

  • If the body of F is an expression, and either D has a void return type or F is async and D has the return type Task, then when each parameter of F is given the type of the corresponding parameter in D, the body of F is a valid expression (w.r.t §11) that would be permitted as a statement_expression (§12.7).
  • If the body of F is a block, and either D has a void return type or F is async and D has the return type Task, then when each parameter of F is given the type of the corresponding parameter in D, the body of F is a valid block (w.r.t §12.3) in which no return statement specifies an expression.

Those are met for both Action and Func<Task> when considering an async lambda expression with no parameters and no return statements which specify expressions.

The fact that the conversion to Func<Task> is deemed better than the conversion to Action looks like it may be missing at the moment. I've raised a bug for this; it may be that there's something I've overlooked though (or that it wasn't specified in C# 7, and has been improved since then).

Upvotes: 3

Related Questions