C#依赖注入实现:从原理到实践


一、依赖注入基础概念

1.1 什么是依赖注入(DI)

依赖注入(Dependency Injection)是一种实现控制反转(IoC)的设计模式,它将对象的创建和绑定从使用对象的地方解耦。在C#中,依赖注入已成为现代应用程序开发的核心模式。

1.2 依赖注入的三种方式

  1. 构造函数注入:最常用的方式,通过构造函数传递依赖
  2. 属性注入:通过公共属性设置依赖
  3. 方法注入:通过特定方法传递依赖

二、.NET Core内置DI容器

2.1 服务注册基础

// 在Startup.cs或Program.cs中配置
services.AddTransient<IMyService, MyService>();  // 每次请求创建新实例
services.AddScoped<IMyRepository, MyRepository>();  // 每个请求范围一个实例
services.AddSingleton<ICacheService, CacheService>();  // 全局单例

2.2 生命周期管理

生命周期描述适用场景
Transient每次请求创建新实例轻量级、无状态服务
Scoped每个请求范围一个实例数据库上下文、请求相关服务
Singleton整个应用生命周期一个实例配置服务、缓存服务

三、高级注册技巧

3.1 多接口实现注册

services.AddTransient<IMessageService, EmailService>();
services.AddTransient<IMessageService, SmsService>();

// 获取所有实现
var services = provider.GetServices<IMessageService>();

3.2 泛型服务注册

services.AddTransient(typeof(IRepository<>), typeof(Repository<>));

3.3 委托工厂注册

services.AddTransient<IService>(provider => 
{
    var config = provider.GetRequiredService<IConfiguration>();
    return new Service(config.GetValue<string>("Setting"));
});

四、构造函数注入实践

4.1 基本用法

public class OrderService
{
    private readonly IOrderRepository _repository;

    public OrderService(IOrderRepository repository)
    {
        _repository = repository;
    }

    public void ProcessOrder(Order order)
    {
        _repository.Save(order);
    }
}

4.2 多依赖处理

public class ReportGenerator
{
    private readonly IDataService _dataService;
    private readonly IFormatter _formatter;

    public ReportGenerator(IDataService dataService, IFormatter formatter)
    {
        _dataService = dataService;
        _formatter = formatter;
    }
}

五、属性与方法注入

5.1 属性注入实现

public class NotificationService
{
    [FromServices]  // 需要Microsoft.AspNetCore.Mvc.Core包
    public IMessageSender MessageSender { get; set; }
}

5.2 方法注入示例

public class DataProcessor
{
    public void Process([FromServices] IValidator validator)
    {
        validator.Validate();
    }
}

六、第三方DI容器集成

6.1 Autofac集成

// Program.cs
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory())
    .ConfigureContainer<ContainerBuilder>(builder =>
    {
        builder.RegisterModule(new MyApplicationModule());
    });

// 模块定义
public class MyApplicationModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<MyService>().As<IMyService>();
    }
}

6.2 Simple Injector集成

var container = new Container();
container.Register<IMyService, MyService>(Lifestyle.Scoped);
container.Verify();

services.AddSimpleInjector(container);

七、DI在ASP.NET Core中的应用

7.1 控制器注入

[ApiController]
[Route("[controller]")]
public class WeatherController : ControllerBase
{
    private readonly IWeatherService _weatherService;

    public WeatherController(IWeatherService weatherService)
    {
        _weatherService = weatherService;
    }

    [HttpGet]
    public ActionResult Get()
    {
        return Ok(_weatherService.GetForecast());
    }
}

7.2 视图中的服务注入

@inject IGreeter Greeter

<h1>@Greeter.GetGreeting()</h1>

八、高级应用场景

8.1 装饰器模式实现

services.AddTransient<IDataService, DataService>();
services.Decorate<IDataService, LoggingDataService>();

8.2 条件注册

services.AddTransient<IMessageService>(provider => 
{
    var env = provider.GetRequiredService<IWebHostEnvironment>();
    return env.IsDevelopment() 
        ? new DebugMessageService() 
        : new ProductionMessageService();
});

8.3 动态解析

public class ProcessorFactory
{
    private readonly IServiceProvider _provider;

    public ProcessorFactory(IServiceProvider provider)
    {
        _provider = provider;
    }

    public IProcessor GetProcessor(string type)
    {
        return type switch
        {
            "A" => _provider.GetRequiredService<ProcessorA>(),
            "B" => _provider.GetRequiredService<ProcessorB>(),
            _ => throw new ArgumentException("Invalid processor type")
        };
    }
}

九、DI最佳实践

  1. 构造函数注入优先:保持代码清晰可测试
  2. 避免服务定位器模式:减少对ServiceProvider的直接使用
  3. 合理使用生命周期:根据服务特性选择适当生命周期
  4. 避免循环依赖:设计扁平化的依赖关系
  5. 接口抽象:依赖抽象而非具体实现
  6. 最小化构造函数参数:保持类的单一职责

十、常见问题解决方案

10.1 循环依赖问题

// 重构为:
public class ServiceA
{
    private readonly IServiceB _b;
    public ServiceA(IServiceB b) { _b = b; }
}

public class ServiceB
{
    private readonly Lazy<IServiceA> _a;
    public ServiceB(Lazy<IServiceA> a) { _a = a; }
}

10.2 多实现选择

services.AddTransient<PrimaryService>();
services.AddTransient<SecondaryService>();

services.AddTransient<IService>(provider => 
    provider.GetRequiredService<PrimaryService>());

10.3 配置选项注入

services.Configure<MyOptions>(Configuration.GetSection("MySettings"));

public class MyService
{
    private readonly MyOptions _options;

    public MyService(IOptions<MyOptions> options)
    {
        _options = options.Value;
    }
}

十一、性能优化技巧

  1. Singleton服务谨慎使用:避免内存泄漏
  2. 避免在Singleton中使用Scoped服务:会导致Scoped服务变为Singleton
  3. 使用TryAdd系列方法:防止重复注册
  4. 延迟初始化:对昂贵资源使用Lazy
  5. 池化技术:对高开销对象使用对象池

十二、单元测试中的DI

12.1 使用Moq模拟依赖

[Fact]
public void TestOrderProcessing()
{
    // Arrange
    var mockRepo = new Mock<IOrderRepository>();
    var service = new OrderService(mockRepo.Object);

    // Act
    service.ProcessOrder(new Order());

    // Assert
    mockRepo.Verify(r => r.Save(It.IsAny<Order>()), Times.Once);
}

12.2 使用测试容器

var services = new ServiceCollection();
services.AddTransient<IMyService, TestMyService>();
var provider = services.BuildServiceProvider();

var service = provider.GetRequiredService<IMyService>();

十三、总结

C#中的依赖注入是现代应用程序开发的核心技术,.NET Core内置的DI容器提供了强大而灵活的功能。通过合理应用DI,可以显著提高代码的可测试性、可维护性和可扩展性。掌握DI的各种实现方式和最佳实践,是成为高级C#开发者的必备技能。


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注