我在泛型方法中有很多有趣的乐趣(希望有乐趣)。在大多数情况下,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
。 - 通过此信息,编译器将具有TResult和TQuery。
解
对我来说,最好的解决方案是更改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
使用什么。在需要做出决定时,它不知道您将把它放进去string
(所以也许应该推断出string
)。即使知道,在法律上也可能是Process<SomeQuery, customClass>
在那里customClass
的任何类衍生string
。另请参阅:返回类型推断不适用于成员组。