.NET依赖注入:泛型注册ListFlattener适配IFlattener<IList<T>>
定义了接口IFlattener<T>,并实现了ListFlattener<T> : IFlattener<IList<T>>类,用于遍历对象并调用其对应的Flattener。目前已注册多个具体Flattener:
ZoneFlattener : IFlattener<Zone>ZoneGroupFlattener : IFlattener<ZoneGroup>OperationFlattener : IFlattener<Operation>
注册代码如下:
services.AddScoped<IFlattener<Zone>, ZoneFlattener>() services.AddScoped<IFlattener<ZoneGroup>, ZoneGroupFlattener>() services.AddScoped<IFlattener<Operation>, OperationFlattener>()
在多处场景中需要处理List<Zone>或List<ZoneGroup>,希望通过IFlattener<IList<T>>统一实现遍历逻辑,避免重复代码。ListFlattener<T>的构造函数依赖IFlattener<T>:
public class ListFlattener<T>(IFlattener<T> flattener) : IFlattener<IList<T>> { }
尝试使用services.AddScoped(typeof(IFlattener<>), typeof(ListFlattener<>))进行泛型注册,但运行时无法解析依赖。不想更换DI容器或改用IListFlattener<T>接口,请问该如何正确注册?
问题核心在于泛型注册的匹配逻辑:AddScoped(typeof(IFlattener<>), typeof(ListFlattener<>))会尝试将IFlattener<X>直接映射到ListFlattener<X>,但你的ListFlattener<T>实现的是IFlattener<IList<T>>,泛型参数不匹配,导致DI容器无法正确解析。
以下两种方案无需修改原有接口或更换DI容器,可解决问题:
方案一:泛型工厂动态注册(推荐)
通过工厂委托明确DI容器的匹配规则:当请求IFlattener<IList<T>>时,创建对应的ListFlattener<T>实例并注入依赖的IFlattener<T>。
// 保留原有非列表类型的Flattener注册 services.AddScoped<IFlattener<Zone>, ZoneFlattener>(); services.AddScoped<IFlattener<ZoneGroup>, ZoneGroupFlattener>(); services.AddScoped<IFlattener<Operation>, OperationFlattener>(); // 注册ListFlattener的泛型适配逻辑 services.AddScoped(typeof(IFlattener<>), (provider, serviceType) => { var genericArg = serviceType.GetGenericArguments()[0]; // 检查当前请求的泛型参数是否为IList<T> if (genericArg.IsGenericType && genericArg.GetGenericTypeDefinition() == typeof(IList<>)) { var elementType = genericArg.GetGenericArguments()[0]; // 构造ListFlattener<T>和对应的IFlattener<T>类型 var flattenerImplType = typeof(ListFlattener<>).MakeGenericType(elementType); var elementFlattenerType = typeof(IFlattener<>).MakeGenericType(elementType); // 获取依赖的IFlattener<T>实例并创建ListFlattener<T> var elementFlattener = provider.GetRequiredService(elementFlattenerType); return Activator.CreateInstance(flattenerImplType, elementFlattener); } // 非IList<T>类型返回null,让DI容器尝试其他注册项 return null; });
方案二:手动批量注册(适合元素类型固定场景)
如果已明确所有需要支持的元素类型,可直接为每个IFlattener<IList<T>>手动注册对应实现:
// 保留原有非列表类型的Flattener注册 services.AddScoped<IFlattener<Zone>, ZoneFlattener>(); services.AddScoped<IFlattener<ZoneGroup>, ZoneGroupFlattener>(); services.AddScoped<IFlattener<Operation>, OperationFlattener>(); // 手动注册每个ListFlattener services.AddScoped(typeof(IFlattener<IList<Zone>>), typeof(ListFlattener<Zone>)); services.AddScoped(typeof(IFlattener<IList<ZoneGroup>>), typeof(ListFlattener<ZoneGroup>)); services.AddScoped(typeof(IFlattener<IList<Operation>>), typeof(ListFlattener<Operation>));
方案对比
- 方案一无需手动维护元素类型列表,新增
IFlattener<T>后自动支持IFlattener<IList<T>>; - 方案二更简洁直接,适合元素类型固定、不会频繁新增的场景。
内容的提问来源于stack exchange,提问作者Birdalicious

