使用泛型类型参数时,不明确的方法会重载

bug*_*d87 5 .net c# generics overload-resolution

考虑以下程序:

using System;
using System.Threading.Tasks;

public class Program
{
    public static void Main()
    {
        var stringTask = Task.FromResult("sample");
        stringTask.TeeAsync(st => Task.CompletedTask).Wait();
    }
}

public static class FunctionalExtensions
{
    public static async Task<T> TeeAsync<T>(this T source, Func<T, Task> asyncAction)
    {       
        await Task.Delay(0); // todo: do something with source

        return source;
    }

    public static async Task<T> TeeAsync<T>(this Task<T> asyncSource, Func<T, Task> asyncAction)
    {
        var source = await asyncSource;

        await Task.Delay(0); // todo: do something with source

        return source;
    }
}
Run Code Online (Sandbox Code Playgroud)

第9行上的编译器错误因为而TeeAsync被调用stringTask

以下方法或属性之间的调用不明确:'FunctionalExtensions.TeeAsync <T>(T,Func <T,Task>)'和'FunctionalExtensions.TeeAsync <T>(Task <T>,Func <T,Task>) "

从每个重载中删除第二个参数突然允许编译器区分第一个参数Task<T>T第一个参数.但是为什么第二个参数 - 两个重载之间相同 - 会导致编译器混淆?

Evk*_*Evk 4

第二个参数不相同。它们都是Func<T, Task>,但T每种情况都不同。

第一次过载有this T source. 这意味着当你这样做时

Task<string> stringTask = Task.FromResult("sample");
stringTask.TeeAsync(...)
Run Code Online (Sandbox Code Playgroud)

对于第一次过载,TTask<string>

第二个有this Task<T> asyncSource. 所以在上面的情况下,第二个过载Tstring

因为你没有在这里指定类型st

stringTask.TeeAsync(st => Task.CompletedTask).Wait();
Run Code Online (Sandbox Code Playgroud)

st可以是Task<string>(第一次过载)或string(第二次)。编译器无法知道你指的是哪一个。如果你这样做:

stringTask.TeeAsync((string st) => Task.CompletedTask).Wait();
Run Code Online (Sandbox Code Playgroud)

它将正确选择第二个。如果你这样做

stringTask.TeeAsync((Task<string> st) => Task.CompletedTask).Wait();
Run Code Online (Sandbox Code Playgroud)

它会首先选择。

有趣的是,如果您实际使用的st方式允许编译器推断它是stringTask<string>- 它就会这样做。例如,这将编译并选择第二个重载:

// we don't specify st type, but using Length property
// which only exists on string
stringTask.TeeAsync(st => Task.FromResult(st.Length)).Wait();
Run Code Online (Sandbox Code Playgroud)

这将首先编译和选择:

// we don't specify st type, but using Result property
// which only exists on Task<string>
stringTask.TeeAsync(st => Task.FromResult(st.Result)).Wait();
Run Code Online (Sandbox Code Playgroud)

但是,如果您使用两者都存在的东西,它将再次(正确地)无法选择重载:

// ToString() exists on both string and Task<string>
// so doesn't help compiler to choose
stringTask.TeeAsync(st => Task.FromResult(st.ToString())).Wait();
Run Code Online (Sandbox Code Playgroud)