为什么C#不推断我的泛型?
通过泛型方法,我有很多Funcy乐趣(有趣的)。 在大多数情况下,C#类型推理非常聪明,可以找出它在我的泛型方法中必须使用的泛型参数,但是现在我已经有了一个C#编译器不成功的设计,而我相信它可以成功找到正确的类型。
任何人都可以告诉我,在这种情况下编译器是否有点愚蠢,或者是否有一个非常明确的理由,为什么它不能推断我的泛型参数?
代码如下:
类和接口定义:
interface IQuery<TResult> { }
interface IQueryProcessor
{
TResult Process<TQuery, TResult>(TQuery query)
where TQuery : IQuery<TResult>;
}
class SomeQuery : IQuery<string>
{
}
一些不能编译的代码:
class Test
{
void Test(IQueryProcessor p)
{
var query = new SomeQuery();
// Does not compile :-(
p.Process(query);
// Must explicitly write all arguments
p.Process<SomeQuery, string>(query);
}
}
为什么是这样? 我在这里错过了什么?
这是编译器的错误信息(它不会让我们想象得太多):
无法根据用法推断方法IQueryProcessor.Process(TQuery)的类型参数。 尝试明确指定类型参数。
我相信C#应该能够推断出它的原因是因为:
IQuery<TResult>
的对象。 IQuery<TResult>
版本是IQuery<string>
,因此TResult必须是string
。 解
对我来说,最好的解决方案是更改IQueryProcessor
接口并在实现中使用动态类型:
public interface IQueryProcessor
{
TResult Process<TResult>(IQuery<TResult> query);
}
// Implementation
sealed class QueryProcessor : IQueryProcessor {
private readonly Container container;
public QueryProcessor(Container container) {
this.container = container;
}
public TResult Process<TResult>(IQuery<TResult> query) {
var handlerType =
typeof(IQueryHandler<,>).MakeGenericType(query.GetType(), typeof(TResult));
dynamic handler = container.GetInstance(handlerType);
return handler.Handle((dynamic)query);
}
}
IQueryProcessor
接口现在接受IQuery<TResult>
参数。 这样它可以返回一个TResult
,这将从消费者的角度解决问题。 我们需要在实现中使用反射来获得实际的实现,因为需要具体的查询类型(在我的情况下)。 但是,这里是动态打字,以帮助我们进行反思。 你可以阅读更多关于这篇文章。
一群人指出C#不会基于约束进行推理。 这是正确的,并且与这个问题有关。 通过检查参数及其相应的形式参数类型来进行推论,这是推理信息的唯一来源。
一群人然后链接到这篇文章:
http://blogs.msdn.com/b/ericlippert/archive/2007/11/05/c-3-0-return-type-inference-does-not-work-on-member-groups.aspx
这篇文章既是过时的,也是与问题无关的。 它过时了,因为它描述了我们在C#3.0中做出的设计决定,然后我们在C#4.0中进行了逆向操作,主要基于对该文章的回应。 我刚刚为该文章添加了更新。
这是无关的,因为这篇文章是关于从方法组参数到泛型委托形式参数的返回类型推断。 这不是原始海报问的情况。
我的相关文章是这样写的:
http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx
C#不会根据泛型方法的返回类型推断泛型类型,而只是泛型方法的参数。
它也不使用约束作为类型推断的一部分,这消除了为你提供类型的通用约束。
有关详细信息,请参阅Eric Lippert关于此主题的文章。
它不使用约束来推断类型。 相反,它会推断类型(如果可能),然后检查约束。
因此,虽然唯一可能的TResult
可以与SomeQuery
参数一起使用,但它不会看到这一点。
还要注意的是, SomeQuery
也可能实现IQuery<int>
,这是编译器的限制可能不是一个坏主意的原因之一。