c# - 是否有可能以强类型方式调用泛型方法,同时保持在编译时推断类型的能力?

标签 c# generics reflection type-inference

我有如下几个接口(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 的签名(即允许推断其通用类型参数)
  • 允许 QueryResponseHandler 调用的编译时类型安全(即没有反射或动态调度)
  • 不更改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/

相关文章:

java - 反射类型检查?

c# - 对多重继承感到困惑

C# List<string> 到带分隔符的字符串

c# - Sitecore.Context.Language 和 Sitecore.Context.ContentLanguage 之间的区别

java - 获取返回泛型类型的类可能吗?

c# - 通过反射设置属性 Nullable<>

c# - 我如何遍历我的类属性并获取它们的类型?

c# - 并非所有程序集都从 bin 文件夹加载到 AppDomain

c# - .NET 2.0 : Sending email to a distribution group

java - 为什么编译器无法推断 Java 中内部类的泛型类型?