考虑以下函数:
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)
这不是编译器的问题——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)