我在泛型方法中有很多有趣的乐趣(希望有乐趣)。在大多数情况下,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<string>
string
解
对我来说,最好的解决方案是更改IQueryProcessor接口并在实现中使用动态类型:
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>参数。这样,它可以返回a 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#7.3稍微改变了何时应用约束的规则,使上述已有十年历史的文章不再准确。有空的时候,我将回顾一下以前同事所做的更改,看看是否值得在我的新博客上发布更正;在此之前,请谨慎使用,并查看C#7.3的实际操作。