为什么C#不推断我的generics?

我有很多有趣的(有趣的)通用的方法。 在大多数情况下,C#types推理是足够聪明的,以找出什么generics参数必须使用我的generics方法,但现在我有一个devise,C#编译器不成功,而我相信它可以成功find正确的types。

任何人都可以告诉我,在这种情况下,编译器是否有点愚蠢,还是有一个非常明确的理由,为什么它不能推断我的generics参数?

代码如下:

类和接口定义:

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)的types参数不能从用法中推断出来。 尝试明确指定types参数。

我相信C#应该能够推断出它的原因是因为:

  1. 我提供了一个实现IQuery<TResult>的对象。
  2. 只有types实现的IQuery<TResult>版本是IQuery<string> ,因此TResult必须是string
  3. 有了这个信息,编译器就有了TResult和TQuery。

对我来说,最好的解决scheme是更改IQueryProcessor接口,并在实现中使用dynamictypes:

 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 ,这将从消费者的angular度来解决问题。 我们需要在实现中使用reflection来获得实际的实现,因为需要具体的查询types(在我的情况下)。 但是,这里来dynamic打字的救援将为我们做反思。 你可以在这篇文章中阅读更多。

一群人指出,C#不会基于约束进行推理。 这是正确的,并与这个问题有关。 通过检查参数和相应的forms参数types来进行推论,这是推理信息的唯一来源。

一群人然后链接到这篇文章:

http://blogs.msdn.com/b/ericlippert/archive/2007/11/05/c-3-0-return-type-in​​ference-does-not-work-on-member-groups.aspx

那篇文章既是过时的,也是与这个问题无关的。 它已经过时了,因为它描述了我们在C#3.0中做出的一个devise决定,然后我们在C#4.0中进行了逆转,主要基于对该文章的回应。 我刚刚为这篇文章添加了一个更新。

这是无关的,因为这篇文章是关于从方法组参数到generics委托forms参数的返回types推断 。 这不是原来的海报问的情况。

我的相关文章是这样写的:

http://blogs.msdn.com/b/ericlippert/archive/2009/12/10/constraints-are-not-part-of-the-signature.aspx

C#不会根据generics方法的返回types来推断genericstypes,而只是generics方法的参数。

它也不使用约束作为types推断的一部分,从而消除了为您提供types的一般约束。

有关详细信息,请参阅Eric Lippert关于此主题的文章 。

它不使用约束来推断types。 相反,它推断types(如果可能),然后检查约束。

因此,虽然唯一可能的TResult可以与SomeQuery参数一起使用,但它不会看到这一点。

还要注意的是, SomeQuery也完全可以实现IQuery<int> ,这是编译器的限制可能不是一个坏主意的原因之一。

这个规范很清楚地说明了这一点:

7.4.2节types推断

如果提供的参数数量不同于方法中的参数数量,则推理立即失败。 否则,假定generics方法具有以下签名:

Tr M(T1 x1 … Tm xm)

通过formsM(E1 … Em)的方法调用,types推断的任务是为每个types参数X1 … Xnfind唯一types参数S1 … Sn,使得调用M(E1 … Em)变得有效。

如您所见,返回types不用于types推断。 如果方法调用不直接映射到types参数,则推论立即失败。

编译器不只是假设你想把string作为TResult参数,也不可以。 想象一下从string派生的TResult 。 两者都是有效的,所以select哪个? 最好是明确的。