C#中如何正确返回匹配泛型接口的依赖注入Handler?
解决Handler类型转换返回null的问题
问题根源分析
- 接口匹配逻辑错误:原代码中用
p.GetType().GetInterfaces().First()获取接口,可能拿到非泛型的IHandler标记接口,导致泛型参数判断失效,甚至引发索引越界异常。 - 泛型协变/逆变不匹配:
IHandler<TRoot, in TMessage>中的TRoot未标记协变(out),若调用GetHandler时传入的TRoot是Handler实际泛型参数的基类,无法直接转换。TMessage是逆变(in)类型,仅允许从抽象消息类型转换到具体消息类型,反向转换会失败。
- 泛型约束不完整:
GetHandler方法中TMessage的约束缺失IMessage,类型检查不严谨。
解决方案
1. 修复Handler匹配逻辑
修改匹配逻辑,确保定位到正确的泛型IHandler<,>接口:
var handlerOrNull = this.Handlers.FirstOrDefault(p => { // 筛选出实现的泛型IHandler<,>接口 var handlerInterface = p.GetType().GetInterfaces() .FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IHandler<,>)); if (handlerInterface == null) return false; var genericArguments = handlerInterface.GetGenericArguments(); return genericArguments[0] == concreteRootType && genericArguments[1] == concreteMessageType; });
2. 调整泛型接口的协变支持
为TRoot添加协变标记(out),允许从具体Root类型转换到基类Root类型:
public interface IHandler<out TRoot, in TMessage> where TRoot : class { TRoot Apply(TRoot root, TMessage message); }
注:TRoot作为方法返回值,使用out协变符合C#泛型规则。
3. 确保调用时传入正确的泛型参数
调用GetHandler时,需传入Handler实际对应的具体TRoot和TMessage类型,而非基类。示例:
var handler = resolver.GetHandler<MyRoot, Message1>(myRoot, message1);
若无法提前确定具体类型,可通过反射动态调用:
var getHandlerMethod = typeof(HandlerResolver).GetMethod(nameof(HandlerResolver.GetHandler)); var genericMethod = getHandlerMethod.MakeGenericMethod(concreteRootType, concreteMessageType); var handler = genericMethod.Invoke(resolver, new object[] { root, message });
4. 补全泛型约束
完善GetHandler方法的泛型约束,保障类型安全:
public IHandler<TRoot, TMessage> GetHandler<TRoot, TMessage>(TRoot root, TMessage message) where TRoot : class where TMessage : class, IMessage // 补全IMessage约束 { // ... 原有逻辑 }
5. 可选:使用动态类型绕过编译时检查
若上述方案无法满足需求,可使用dynamic直接调用Apply方法,无需转换为泛型接口:
dynamic handler = handlerOrNull; return handler.Apply((dynamic)root, (dynamic)message);
注:此方式会失去编译时类型检查,需谨慎使用。
内容的提问来源于stack exchange,提问作者Astrophage
相关产品推荐
相关产品推荐

