当前位置: 首页 > news >正文

C#依赖注入

一、什么是依赖注入?

依赖指的是:当一个类(A)需要另一个类(B)的功能才能完成工作时,A 就 "依赖" 于 B。
例如:OrderService需要用Logger记录日志,那么OrderService依赖于Logger

依赖注入的核心思想是:将依赖的创建和管理交给外部容器,而不是在类内部自行创建。
简单说就是 "谁需要依赖,就由外部把依赖传给它",而非自己创建。
 

二、为什么需要依赖注入?

先看一个 "没有依赖注入" 的反例:
// 日志类
public class Logger
{public void Log(string message) => Console.WriteLine($"日志:{message}");
}// 订单服务(依赖Logger)
public class OrderService
{// 自己创建依赖(问题所在)private readonly Logger _logger = new Logger();public void CreateOrder(){_logger.Log("订单创建成功");// 其他业务逻辑...
    }
}
View Code

这个例子中OrderService直接依赖Logger的具体实现,如果未来要替换为FileLogger,必须修改OrderService,这就违反了开闭原则,对扩展开放,对修改关闭

如果使用依赖注入,应该要生命一个抽象接口,让服务类去依赖接口,以后要添加别的类型的日志的适合直接添加就行,然后在逻辑代码中实例化接口,传入给服务类的构造函数。

// 抽象接口(依赖抽象)
public interface ILogger
{void Log(string message);
}// 具体实现
public class ConsoleLogger : ILogger
{public void Log(string message) => Console.WriteLine($"日志:{message}");
}// 订单服务(依赖通过外部注入)
public class OrderService
{private readonly ILogger _logger;// 构造函数注入(推荐方式)public OrderService(ILogger logger){_logger = logger; // 依赖由外部传入,而非自己创建
    }public void CreateOrder(){_logger.Log("订单创建成功");}
}
View Code

三、依赖注入的 3 种方式

在 C# 中,依赖注入通常通过以下 3 种方式实现:

1. 构造函数注入(最推荐)

通过类的构造函数接收依赖,是.NET 中最常用的方式。适用于 "必须的依赖"(类没有它就无法工作)。
 
public class UserService
{private readonly ILogger _logger;// 构造函数注入:创建UserService时必须提供ILoggerpublic UserService(ILogger logger){_logger = logger ?? throw new ArgumentNullException(nameof(logger));}
}
View Code

2. 属性注入

通过公共属性接收依赖,适用于 "可选的依赖"(类没有它也能工作)。
在ASP.NET Core 中,通常配合[Inject]特性使用(如在视图中)
public class UserService
{// 属性注入:依赖是可选的public ILogger Logger { get; set; }public void DoWork(){Logger?.Log("执行工作"); // 注意判空
    }
}
View Code

3. 方法注入

通过方法参数接收依赖,适用于仅在特定方法中需要的依赖。
public class UserService
{public void DoWork(ILogger logger){logger.Log("执行工作"); // 仅该方法需要依赖
    }
}
View Code

 

四、微软自带的Microsoft.Extensions.DependencyInjection

1.核心原理是通过服务容器管理服务的注册、生命周期和依赖解析,其设计遵循 “依赖倒置” 和 “控制反转” 原则,核心流程可分为服务注册、容器构建和服务解

using Microsoft.Extensions.DependencyInjection;// 定义服务接口和实现
public interface IUserService { }
public class UserService : IUserService { }// 注册服务其实就是一个LIst<ServiceDescriptor>集合
var services = new ServiceCollection();
// 瞬时生命周期,new一个新的元素,参数:抽象类型为接口,具体实现类型,Transient是生命周期
services.AddTransient<IUserService, UserService>();
// 作用域生命周期
services.AddScoped<IUserService, UserService>();
// 单例生命周期
services.AddSingleton<IUserService, UserService>();

// 3. 构建服务提供器(DI容器)
_serviceProvider = services.BuildServiceProvider();

2.容器构建(BuildServiceProvider

当服务注册完成后,调用 services.BuildServiceProvider() 会创建 IServiceProvider 实例(默认实现为 ServiceProvider)。这一步是 “从注册到可用容器” 的关键转换,核心工作包括:

验证服务注册的合法性

为每个服务创建 “解析器”

初始化根容器与作用域管理

3.服务解析(GetService/GetRequiredService

当调用 serviceProvider.GetRequiredService<T>() 时,ServiceProvider 会按以下流程解析服务实例:

查找服务元数据

根据生命周期创建实例

 自动注入依赖(构造函数注入)

 处理 IDisposable 服务

4.最后总结原理就类似 :

1)注册阶段:new一个字典集合,注册:相当于添加元素,添加接口和实现类指定生命周期方式

2)构建阶段:BuildServiceProvider() 验证注册合法性,生成解析器,初始化根容器。

3)解析阶段:

据服务类型查找 ServiceDescriptor。按生命周期(Transient/Scoped/Singleton)创建实例,递归注入依赖。缓存实例(Scoped 缓存于作用域,Singleton 缓存于根容器)。

4)清理阶段:作用域或根容器释放时,自动清理 IDisposable 服务。

http://www.gsyq.cn/news/8531.html

相关文章:

  • 完整教程:Docker Compose 一键启动多容器服务
  • [MCP][07]logging和progress等功能说明
  • 端口命令
  • c-store发送dcm文件超时
  • 【2025-09-19】连岳摘抄
  • MAUI和Android、IOS的互操作之IOS篇
  • 【Java】Hashtable讲解
  • 【JAVA】TreeSet讲解
  • 批判 vs 审判
  • ctfshow web入门 SSRF
  • C#中避免GC压力和提高性能的8种技术
  • UNIX网络编程笔记:共享内存区和远程过程调用 - 指南
  • 函数内联
  • G. Chimpanzini Bananini
  • ERP和MES、WMS、CRM,到底怎么配合 - 智慧园区
  • 底层
  • WPF 视图缩略图控件(支持缩放调节与拖拽定位)
  • javaScript(WebAPI) - 教程
  • es中的端点
  • es中的数据类型
  • 03作业
  • 软工作业个人项目
  • YOLO进阶提升 6模型训练与测试
  • 解码C语言位字段
  • Sql Server 多层嵌套事务的执行结果
  • es入门
  • 02-Media-7-uvc.py 应用软件解码的USB摄像头(UVC)捕获视频并显示的程序
  • YOLO入门理解 评估指标
  • [ICPC 2024 Yokohama R] Peculiar Protocol
  • The 2025 ICPC Asia East Continent Online Contest (II)(C,D,E,H,I)