You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多态与依赖注入:如何无需实例化即可选择子类

如何在依赖注入(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:37:27