The second parameters are not identical. They are both Func<T, Task> , but T is different in each case.
The first overload has this T source . That means when you do
Task<string> stringTask = Task.FromResult("sample"); stringTask.TeeAsync(...)
for the first overload, T is Task<string> .
The second has this Task<T> asyncSource . Therefore, in the above case, for the second overload T there is string .
Since you do not specify type st here:
stringTask.TeeAsync(st => Task.CompletedTask).Wait();
st can be either Task<string> (first overload) or string (second). The compiler cannot know what you had in mind. If you do:
stringTask.TeeAsync((string st) => Task.CompletedTask).Wait();
He will choose the second one correctly. And if you do
stringTask.TeeAsync((Task<string> st) => Task.CompletedTask).Wait();
he will choose first.
Interestingly, if you really use st in such a way that the compiler can determine if it will be a string or Task<string> , it will do it. For example, this will compile and select the second overload:
And this compiles and is selected first:
But if you use something that exists on both, it again (correctly) will not be able to select overload:
source share