当前位置:首页 > C# > 正文

C#依赖注入的装饰器模式扩展(深入理解.NET Core中DI与装饰器的优雅结合)

在现代C#开发中,依赖注入(Dependency Injection, DI) 已成为构建可测试、可维护应用程序的核心技术。而装饰器模式(Decorator Pattern) 则是一种结构型设计模式,用于在不修改原始类的前提下动态地给对象添加新功能。将二者结合,可以实现高度灵活且解耦的系统架构。

本文将手把手教你如何在 .NET Core 或 .NET 5+ 项目中,利用 C#依赖注入.NET Core扩展方法 实现装饰器模式,并通过 DI容器 自动装配装饰链。

C#依赖注入的装饰器模式扩展(深入理解.NET Core中DI与装饰器的优雅结合) C#依赖注入 装饰器模式 .NET Core扩展方法 DI容器 第1张

什么是装饰器模式?

装饰器模式允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。它比继承更灵活,因为你可以动态组合多个装饰器。

为什么要在依赖注入中使用装饰器?

在实际项目中,我们经常需要为服务添加日志、缓存、权限校验、性能监控等功能。如果把这些逻辑硬编码到每个服务中,会导致代码重复、难以维护。而使用装饰器 + DI,可以将这些横切关注点(cross-cutting concerns)模块化,并通过配置自动“包裹”目标服务。

实战:用C#实现一个带日志的装饰器

假设我们有一个简单的 IOrderService 接口:

public interface IOrderService{    Task<string> PlaceOrderAsync(string orderId);}

它的默认实现如下:

public class OrderService : IOrderService{    public async Task<string> PlaceOrderAsync(string orderId)    {        // 模拟下单逻辑        await Task.Delay(100);        return $"订单 {orderId} 已创建";    }}

现在,我们想在每次调用 PlaceOrderAsync 时自动记录日志。我们可以创建一个日志装饰器:

public class LoggingOrderServiceDecorator : IOrderAssistant{    private readonly IOrderService _innerService;    private readonly ILogger<LoggingOrderServiceDecorator> _logger;    public LoggingOrderServiceDecorator(        IOrderService innerService,        ILogger<LoggingOrderServiceDecorator> logger)    {        _innerService = innerService;        _logger = logger;    }    public async Task<string> PlaceOrderAsync(string orderId)    {        _logger.LogInformation("开始下单,订单ID: {OrderId}", orderId);        var result = await _innerService.PlaceOrderAsync(orderId);        _logger.LogInformation("下单成功: {Result}", result);        return result;    }}

注意:装饰器实现了相同的接口,并在其构造函数中接收被装饰的实例(即 _innerService),从而形成“链式调用”。

通过扩展方法注册装饰器

为了简化注册过程,我们可以编写一个 .NET Core扩展方法,让 DI 容器自动将装饰器“包裹”在原始服务外面。

public static class ServiceCollectionExtensions{    public static IServiceCollection AddDecorator<TService, TDecorator>(        this IServiceCollection services)        where TService : class        where TDecorator : class, TService    {        // 先获取已注册的服务描述        var descriptor = services.FirstOrDefault(d => d.ServiceType == typeof(TService));        if (descriptor != null)        {            // 移除原始注册            services.Remove(descriptor);                        // 重新注册:先注册原始实现为 TService            services.Add(new ServiceDescriptor(                typeof(TService),                descriptor.ImplementationType ?? descriptor.ImplementationFactory,                descriptor.Lifetime));            // 再用装饰器包裹它            services.Add(new ServiceDescriptor(                typeof(TService),                sp => ActivatorUtilities.CreateInstance<TDecorator>(sp, sp.GetRequiredService<TService>()),                descriptor.Lifetime));        }        return services;    }}

然后在 Program.cs(或 Startup.cs)中这样注册:

var builder = WebApplication.CreateBuilder(args);// 注册原始服务builder.Services.AddScoped<IOrderService, OrderService>();// 添加日志装饰器builder.Services.AddDecorator<IOrderService, LoggingOrderServiceDecorator>();var app = builder.Build();// ...

现在,每当从 DI 容器请求 IOrderService 时,实际得到的是 LoggingOrderServiceDecorator 的实例,而它内部又持有真正的 OrderService。这就是装饰器链的威力!

进阶:多层装饰器

你可以叠加多个装饰器。例如,再加一个缓存装饰器:

builder.Services.AddScoped<IOrderService, OrderService>();builder.Services.AddDecorator<IOrderService, CachingOrderServiceDecorator>();builder.Services.AddDecorator<IOrderService, LoggingOrderServiceDecorator>();

此时调用顺序为:
Logging → Caching → Original Service
注意:注册顺序决定了装饰顺序,后注册的在外层。

总结

通过结合 C#依赖注入装饰器模式.NET Core扩展方法,我们可以构建出高度模块化、可插拔的应用程序。这种模式特别适合处理日志、缓存、验证、重试等通用功能,避免了代码重复,提升了系统的可维护性和可测试性。

记住,良好的架构不是一蹴而就的,但掌握这些核心模式和 DI容器 的高级用法,将为你打下坚实的基础。

希望这篇教程能帮助你轻松上手 C# 中的装饰器与依赖注入!欢迎在项目中实践并分享你的经验。