当有泛型返回值时,为什么编译器不能推断泛型参数?

Mat*_*ero 3 .net c# generics

考虑以下函数:

public void DoSomething<TSource>(TSource data)
{
   // ...
}
Run Code Online (Sandbox Code Playgroud)

在 C# 中,编译器可以TSource通过检查方法的参数来隐式推断的类型:

DoSomething("Hello") // Works fine. DoSomething<string>("Hello") is called.
Run Code Online (Sandbox Code Playgroud)

当有通用返回值时,为什么我们不能做同样的事情?

例如:

public TResult DoSomething<TResult, TSource>(TSource data)
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

TResult无法推断(我明白为什么),但编译器肯定可以推断出TSource的类型,不是吗?。

但是,这不会编译:

int result = DoSomething<int>("Hello"); // This should call DoSomething<int,string>("Hello")
Run Code Online (Sandbox Code Playgroud)

The*_*kis 5

这不是编译器的问题——C# 要求你要么明确指定所有类型参数,要么让它推断所有类型参数。

使用您尝试过的语法没有中间立场,我想这是因为如果您有这样的通用方法:

public void DoSomething<T1, T2>(T1 data, T2 data)
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

你是这样使用它的:

var obj1 = "Hello!";
var obj2 = "Hello?";
DoSomething<IEnumerable<char>>(obj1, obj2);
Run Code Online (Sandbox Code Playgroud)

最后一行可能是两个同样有效的东西的简写:

DoSomething<string, IEnumerable<char>>(obj1, obj2);
DoSomething<IEnumerable<char>, string>(obj1, obj2);
Run Code Online (Sandbox Code Playgroud)

必须采用不同的语法(如<string, ?>)或额外的推理规则,以使这种情况有意义且无歧义。我想设计团队认为这不值得。


请注意,如果您确实需要部分泛型类型推断,则有一种常见的模式将调用拆分为两个调用,并使用一个辅助对象来保存调用之间的信息。这本质上是柯里化,应用于类型参数。

我将以使用公共接口和私有实现的形式展示该模式,但如果您不关心这一点,则可以完全跳过该接口。

public TResult DoSomething<TResult, TSource>(TSource data)
{
    // ...
}
Run Code Online (Sandbox Code Playgroud)

会成为:

public IWrapper<TSource> DoSomething<TSource>(TSource data)
{
    return new WrapperImplementation<TSource>(data);
}
Run Code Online (Sandbox Code Playgroud)

在哪里:

public interface IWrapper<T>
{
    TResult Apply<TResult>();
}

class WrapperImplementation<T> : IWrapper<T>
{
    private readonly T _source;

    public WrapperImplementation(T source)
    {
        _source = source;
    } 

    public TResult Apply<TResult>()
    {
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

用法是:

DoSomething("Hello").Apply<int>();
Run Code Online (Sandbox Code Playgroud)