.NET多态依赖注入:无需手动注册父类型的配置方案
多态依赖注入的自动注册解决方案
问题描述
定义IPlugin接口,以及实现该接口(同时还实现其他接口)的ConcretePlugin类;另外有两个依赖注入类:
TestConcrete:构造函数直接依赖ConcretePlugin实例TestList:构造函数依赖IEnumerable<IPlugin>
在使用Microsoft.Extensions.DependencyInjection和Simple Injector测试时,遇到两难问题:
- 仅注册
ConcretePlugin为单例:TestList的构造函数会得到空的IEnumerable<IPlugin>,框架未将ConcretePlugin关联到IPlugin类型 - 把实例注册为
IPlugin类型:TestConcrete无法被实例化,框架找不到ConcretePlugin类型的注册
手动为ConcretePlugin实现的每一个父类型(包括所有接口)单独注册实例过于繁琐——每次新增接口实现都得更新DI注册代码,完全不符合开闭原则。而纯DI场景下,根本不需要显式声明实例的父类型,就能自动完成多态注入,这让DI框架的原生支持显得很鸡肋。
期望DI框架能通过懒加载检查已注册类型是否是请求类型的子类型,自动完成注入,而非要求开发者手动注册所有父类型。至于循环引用(比如TestList实现IPlugin)这类本身就不合理的场景,完全不需要框架考虑。
针对Microsoft.Extensions.DependencyInjection的解决方案
编写扩展方法,自动扫描目标类型的所有接口,将同一个实例注册到所有父类型:
public static IServiceCollection RegisterWithAllInterfaces<T>(this IServiceCollection services, ServiceLifetime lifetime) where T : class { var type = typeof(T); // 获取类型实现的所有接口 var interfaces = type.GetInterfaces(); // 先注册自身类型 services.Add(new ServiceDescriptor(type, type, lifetime)); // 为每个接口注册,指向同一个实例 foreach (var iface in interfaces) { services.Add(new ServiceDescriptor(iface, sp => sp.GetRequiredService(type), lifetime)); } return services; }
使用方式:
services.RegisterWithAllInterfaces<ConcretePlugin>(ServiceLifetime.Singleton);
注册后,无论是请求ConcretePlugin还是IPlugin(或其他实现的接口),都会拿到同一个单例实例;TestList的IEnumerable<IPlugin>也会自动包含该实例。
针对Simple Injector的解决方案
Simple Injector可通过自定义注册逻辑实现类似效果:
public static void RegisterWithAllInterfaces<T>(this Container container, Lifestyle lifestyle) where T : class { var type = typeof(T); var instanceProducer = container.Register(type, type, lifestyle); foreach (var iface in type.GetInterfaces()) { container.Register(iface, () => instanceProducer.GetInstance(), lifestyle); } }
使用方式:
container.RegisterWithAllInterfaces<ConcretePlugin>(Lifestyle.Singleton);
原理说明
这类扩展方法的核心逻辑:
- 先注册目标类型本身
- 通过反射获取目标类型实现的所有接口(可扩展支持基类)
- 将每个父类型注册为指向目标类型的实例,确保所有请求都拿到同一个实例
这种方式既满足多态注入需求,又避免手动维护所有父类型的注册代码,符合开闭原则。
内容的提问来源于stack exchange,提问作者Stelios Adamantidis
相关产品推荐
相关产品推荐

