多态与依赖注入:如何无需实例化即可选择子类
如何在依赖注入(DI)中根据条件选择具体实现类
首先先帮你修正下原始代码里的几个小问题,不然会编译报错:
SuperClass定义了抽象方法DoSomething(),但类本身没标记为abstract- 子类的构造函数不需要重复声明
_dependency字段,直接通过base调用父类构造函数传递依赖即可
修正后的基础代码:
public interface IMyInterface { void DoSomething(); } public abstract class SuperClass : IMyInterface { protected DependencyClass _dependency; public SuperClass(DependencyClass dependency) { _dependency = dependency; } public abstract void DoSomething(); } public class ChildClassCommon : SuperClass { public ChildClassCommon(DependencyClass dependency) : base(dependency) { } public override void DoSomething() { // 通用逻辑实现 } } public class ChildClassSpecial : SuperClass { public ChildClassSpecial(DependencyClass dependency) : base(dependency) { } public override void DoSomething() { // 特殊逻辑实现 } }
接下来解决你的核心问题:在遵循DI原则的前提下,根据条件选择具体实现类,而非手动 new。最常用且符合DI设计理念的方式是引入工厂模式,把实例化逻辑从业务类剥离到专门的工厂中,同时让工厂本身也通过DI注入。
方案1:自定义工厂类(推荐)
创建一个工厂类,负责根据传入的条件返回对应的 IMyInterface 实例。工厂的依赖也通过构造函数注入,完全遵循DI规则。
工厂实现
public interface IMyInterfaceFactory { IMyInterface Create(string recordType); } public class MyInterfaceFactory : IMyInterfaceFactory { private readonly DependencyClass _dependency; // 工厂的依赖由DI注入 public MyInterfaceFactory(DependencyClass dependency) { _dependency = dependency; } public IMyInterface Create(string recordType) { return recordType switch { "Common" => new ChildClassCommon(_dependency), "Special" => new ChildClassSpecial(_dependency), _ => throw new ArgumentOutOfRangeException(nameof(recordType), "不支持的记录类型") }; } }
修改Main类,注入工厂而非直接注入IMyInterface
public class MainClass { private readonly IMyInterfaceFactory _factory; // 注入工厂,而非具体的IMyInterface实例 public MainClass(IMyInterfaceFactory factory) { _factory = factory; } public void Selector(string recordType) { // 通过工厂获取对应实例,无需手动处理依赖 var myClass = _factory.Create(recordType); myClass.DoSomething(); } }
方案2:利用DI容器的服务注册(适合使用容器的场景)
如果你用的是Microsoft.Extensions.DependencyInjection、Autofac这类DI容器,可以注册多个 IMyInterface 的实现,再结合容器来获取对应实例。
比如用Microsoft.Extensions.DependencyInjection的示例:
注册服务
var services = new ServiceCollection(); services.AddSingleton<DependencyClass>(); services.AddTransient<ChildClassCommon>(); services.AddTransient<ChildClassSpecial>(); services.AddTransient<IMyInterfaceFactory, MyInterfaceFactory>();
如果不想写自定义工厂,也可以在Main类中注入 IServiceProvider(不推荐,因为会引入服务定位器反模式,但简单场景下可用):
public class MainClass { private readonly IServiceProvider _serviceProvider; public MainClass(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public void Selector(string recordType) { IMyInterface myClass = recordType switch { "Common" => _serviceProvider.GetRequiredService<ChildClassCommon>(), "Special" => _serviceProvider.GetRequiredService<ChildClassSpecial>(), _ => throw new ArgumentOutOfRangeException(nameof(recordType)) }; myClass.DoSomething(); } }
为什么这两种方案符合DI原则?
- 业务类(MainClass)不再手动实例化依赖,所有依赖都通过注入获取
- 实例化逻辑被封装在工厂或容器中,符合单一职责原则
- 保持了代码的可测试性:你可以轻松Mock工厂或替换DI容器中的实现来做单元测试
内容的提问来源于stack exchange,提问作者wysiwyg
相关产品推荐
相关产品推荐

