I don't understand why C# ends up executing an incorrect extension method in the following LINQPad code:
void Main()
{
// Actual: Sync Action
"Expected: Sync Action".Run(x => { x.Dump(); });
// Actual: Async Task
"Expected: Async Task".Run(async x => { await System.Threading.Tasks.Task.Run(() => x.Dump()); });
// Actual: Async Task!!
"Expected: Sync Action".Run(x => { throw new Exception("Meh"); });
}
static class Extensions
{
public static void Run<T>(this T instance, Action<T> action)
{
"Actual: Sync Action".Dump();
action(instance);
}
public static void Run<T>(this T instance, Func<T, System.Threading.Tasks.Task> func)
{
"Actual: Async Task".Dump();
func(instance).Wait();
}
}
Why does the compiler think that the lambda returns a Task here?
I expected to see "Actual: Sync Action" in the third call to Run() since nothing in the lambda indicates that this is a Func returning Task.
This is simply an overload resolution issue. Clearly, the lambda
x => { throw new Exception("Meh"); }
can be converted to either anAction<T>
or to aFunc<T, SomeNonVoidType>
(as well as to many other delegate types irrelevant to this question). It's simply C#'s overload resolution rules that prefer the latter in this case.Here's a more representative example:
As for why this is the case, I'm not 100% sure, but a casual look at the spec shows me the below likely explanation (emphasis mine):