我有如下几个接口(interface),用于调解查询对象和查询处理程序:-
public interface IQuery<TResponse> { }
public interface IQueryBus
{
public TResponse Resolve<TResponse>(IQuery<TResponse> query);
}
它应该在 Controller 层调用如下:-
FooQuery query = ... // comes from model binding
FooResponse response = this.bus.Resolve(query);
总线负责为查询定位处理程序:-
public interface IQueryHandler<in TQuery, out TResponse>
where TQuery : IQuery<TResponse>
{
public TResponse Handle(TQuery query);
}
处理程序使用依赖项解析器进行连接(我正在使用 Ninject,但这应该不是很重要)。
我目前的实现是按照以下几行:-
public TResponse Resolve(IQuery<TResponse> query)
{
var queryType = query.GetType();
var handlerType = typeof(IQueryHandler<,>)
.MakeGenericType(queryType, typeof(TResponse));
var handleMethod = handlerType.GetMethod("Handle", ...);
var handler = kernel.Get(handlerType);
return (TResponse)handleMethod.Invoke(handler, new[] { query });
}
我的问题是,出于风格和性能方面的原因,我想避免调用 MethodInfo.Invoke
。
如果我将 IQueryBus.Resolve
的签名更改为:-
public TResponse Resolve<TQuery, TResponse>(TQuery query)
where TQuery : IQuery<TResponse>
那么改变实现就很简单了:-
var handler = kernel.GetType<IQueryHandler<TQuery, TResponse>>();
return handler.Handle(query);
但是当然不能在使用时推断泛型类型参数:-
FooResponse response = this.bus.Resolve<FooQuery, FooResponse>(query);
我可以吃我的蛋糕吗?是否有满足以下条件的解决方案:-
- 不更改
IQueryBus.Resolve
的签名(即允许推断其通用类型参数) - 允许
Query
、Response
和Handler
调用的编译时类型安全(即没有反射或动态调度) - 不更改
IQueryHandler
的接口(interface)(即不将处理程序暴露给总线的实现约束)
我可以控制其余代码,我可以引入新类型并完全替换 IQueryBus
的实现。我可以在应用程序启动时缓存依赖项解析器完成的工作(这是我拥有的最有前途的 atm)。
如果最坏的情况发生在最坏的情况下,那么我想要求调用者指定泛型类型参数并不是世界末日,但我希望有一些更优雅的东西来最大限度地减少 future 出错的机会.
ShortBus on github 中有一个非常相似的实现如果您正在寻找更完整的真实示例。
最佳答案
如果不可能有多个 Queries
使用相同的 TResult
,那么这样的事情是可能的。
public interface IQueryHandler<out TResponse>
{
TResponse Handle(object query);
}
public abstract class QueryHandler<TQuery, TResponse> : IQueryHandler<TResponse>
{
TResponse IQueryHandler<TResponse>.Handle(object query)
{
return Handle((TQuery)query);
}
protected abstract TResponse Handle(TQuery query);
}
public TResponse Resolve<TResponse>(IQuery<TResponse> query)
{
var handlerType = typeof(IQueryHandler<TResponse>);
IQueryHandler<TResponse> handler = kernel.Get(handlerType);
return handler.Handle(query);
}
您可能会在中间稍微松散输入,但特定的查询和处理程序都像以前一样正确输入。当 TResponse
是值类型时,我看到的唯一性能影响是装箱的可能性。但 IoC 正在寻找具体的处理程序,这会掩盖这一点。
关于c# - 是否有可能以强类型方式调用泛型方法,同时保持在编译时推断类型的能力?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21610038/