The Problem
Consider these two extension methods which are just a simple map from any type T1
to T2
, plus an overload to fluently map over Task<T>
:
public static class Ext {
public static T2 Map<T1, T2>(this T1 x, Func<T1, T2> f)
=> f(x);
public static async Task<T2> Map<T1, T2>(this Task<T1> x, Func<T1, T2> f)
=> (await x).Map(f);
}
Now, when I use the second overload with a mapping to a reference type...
var a = Task
.FromResult("foo")
.Map(x => $"hello {x}"); // ERROR
var b = Task
.FromResult(1)
.Map(x => x.ToString()); // ERROR
...I get the following error:
CS0121: The call is ambiguous between the following methods or properties: 'Ext.Map(T1, Func)' and 'Ext.Map(Task, Func)'
Mapping to a value type works fine:
var c = Task
.FromResult(1)
.Map(x => x + 1); // works
var d = Task
.FromResult("foo")
.Map(x => x.Length); // works
But only as long the mapping actually uses the input to produce an output:
var e = Task
.FromResult(1)
.Map(_ => 0); // ERROR
The Question
Can anyone please explain to me what is going on here? I've already given up on finding a feasible fix for this error, but at least I'd like to understand the root cause of this mess.
Additional Notes
So far I found three workarounds which are unfortunately not acceptable in my use case. The first is to specify the type arguments of Task<T1>.Map<T1,T2>()
explicitly:
var f = Task
.FromResult("foo")
.Map<string, string>(x => $"hello {x}"); // works
var g = Task
.FromResult(1)
.Map<int, int>(_ => 0); // works
Another workaround is to not use lambdas:
string foo(string x) => $"hello {x}";
var h = Task
.FromResult("foo")
.Map(foo); // works
And the third option is to restrict the mappings to endofunctions (i.e. Func<T, T>
):
public static class Ext2 {
public static T Map2<T>(this T x, Func<T, T> f)
=> f(x);
public static async Task<T> Map2<T>(this Task<T> x, Func<T, T> f)
=> (await x).Map2(f);
}
I created a .NET Fiddle where you can try out all the above examples yourself.