C#依赖注入
一、什么是依赖注入?
例如:
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("订单创建成功"); // 其他业务逻辑... } }
这个例子中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("订单创建成功"); } }
三、依赖注入的 3 种方式
1. 构造函数注入(最推荐)

public class UserService { private readonly ILogger _logger; // 构造函数注入:创建UserService时必须提供ILogger public UserService(ILogger logger) { _logger = logger ?? throw new ArgumentNullException(nameof(logger)); } }
2. 属性注入

public class UserService { // 属性注入:依赖是可选的 public ILogger Logger { get; set; } public void DoWork() { Logger?.Log("执行工作"); // 注意判空 } }
3. 方法注入

public class UserService { public void DoWork(ILogger logger) { logger.Log("执行工作"); // 仅该方法需要依赖 } }
四、微软自带的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 服务。
四、依赖注入在WPF中的应用
1.第一步删除App.xaml文件中删除StartupUri="MainWindow.xaml"
2.定义接口

using System; namespace 依赖注入包典型应用 { /// <summary> /// 定义服务接口 /// </summary> public interface IDataService { string GetData(); string RefreshData(); } /// <summary> /// Log服务接口 /// </summary> public interface ILoggerService { void Log(string message); void LogError(string errorMessage); } /// <summary> /// 设定服务接口 /// </summary> public interface ISettingsService { string GetSetting(string key); void SetSetting(string key, string value); } }
3.定义基于接口的数据服务

using System.IO; namespace 依赖注入包典型应用 { public class DataService : IDataService { // 可以在这里注入其他服务,如数据库访问服务等 private int _refreshCount = 0; public string GetData() { return "Initial data from DataService - " + $"Current time: {DateTime.Now:HH:mm:ss}"; } public string RefreshData() { _refreshCount++; return $"Refreshed data ({_refreshCount}) - " + $"Current time: {DateTime.Now:HH:mm:ss}"; } } public class LoggerService : ILoggerService { public void Log(string message) { var logMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] INFO: {message}{Environment.NewLine}"; Console.WriteLine(logMessage); // 输出到控制台 WriteToLogFile(logMessage); // 写入日志文件 } public void LogError(string errorMessage) { var logMessage = $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] ERROR: {errorMessage}{Environment.NewLine}"; Console.WriteLine(logMessage); // 输出到控制台 WriteToLogFile(logMessage); // 写入日志文件 } private void WriteToLogFile(string message) { try { var logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "app.log"); File.AppendAllText(logPath, message); } catch { // 日志写入失败时的处理 } } } public class SettingsService : ISettingsService { // 实际应用中可能会从配置文件或注册表读取设置 private readonly Dictionary<string, string> _settings; public SettingsService() { // 初始化一些默认设置 _settings = new Dictionary<string, string> { { "AppName", "WPF DI Example" }, { "Version", "1.0.0" }, { "Theme", "Light" } }; } public string GetSetting(string key) { if (_settings.TryGetValue(key, out var value)) { return value; } return null; } public void SetSetting(string key, string value) { if (_settings.ContainsKey(key)) { _settings[key] = value; } else { _settings.Add(key, value); } // 在实际应用中,这里应该将设置保存到持久化存储 } } }
4.创建,注册,解析

using Microsoft.Extensions.DependencyInjection; using System.Windows; namespace 依赖注入包典型应用 { public partial class App : Application { private readonly IServiceProvider _serviceProvider; public App() { // 配置服务集合 var serviceCollection = new ServiceCollection(); ConfigureServices(serviceCollection); // 容器构建:验证服务注册的合法性,为每个服务创建 “解析器”,初始化根容器与作用域管理 _serviceProvider = serviceCollection.BuildServiceProvider(); } private void ConfigureServices(IServiceCollection services) { // 注册窗口 services.AddSingleton<MainWindow>(); // 注册服务 services.AddScoped<IDataService, DataService>();//范围,在作用域内(IServiceScope )缓存实例,同一作用域内复用,比如流程 services.AddTransient<ILoggerService, LoggerService>();//瞬时,每次解析服务时都会创建新实例,用于轻量级服务。如小工具,计算格式化等 services.AddSingleton<ISettingsService, SettingsService>();//单例,在整个应用程序的生命周期中只创建一次实例。比如唯一硬件(工厂模式下的相机)长连接模式,配置管理等 } protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); // 服务解析:查找服务元数据,根据生命周期创建实例,自动注入依赖(构造函数注入) var mainWindow = _serviceProvider.GetRequiredService<MainWindow>(); mainWindow.Show(); } } }
5.界面代码。界面代码没什么可以看的

<Window x:Class="依赖注入包典型应用.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:依赖注入包典型应用" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800"> <Grid Margin="20"> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <!--后台代码使用--> <TextBlock x:Name="DataDisplay" TextWrapping="Wrap" Margin="0,0,0,20"/> <Button x:Name="RefreshButton" Content="Refresh Data" Click="RefreshButton_Click" Grid.Row="1" HorizontalAlignment="Left" Padding="10,5"/> </Grid> </Window>
6.界面后台代码,使用已注册的服务

using System.Text; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; namespace 依赖注入包典型应用 { /// <summary> /// Interaction logic for MainWindow.xaml /// </summary> public partial class MainWindow : Window { private readonly IDataService _dataService; private readonly ILoggerService _loggerService; private readonly ISettingsService _settingsService; // 构造函数依赖注入 public MainWindow(IDataService dataService, ILoggerService loggerService, ISettingsService settingsService) { InitializeComponent(); _dataService = dataService; _loggerService = loggerService; _settingsService = settingsService; // 使用注入的服务 Loaded += MainWindow_Loaded; } private void MainWindow_Loaded(object sender, RoutedEventArgs e) { try { // 使用设置服务//单例的 实例 var appName = _settingsService.GetSetting("AppName"); Title = appName; // 使用数据服务//范围的实例 var data = _dataService.GetData(); DataDisplay.Text = data; // 使用日志服务//瞬时的实例 _loggerService.Log("Main window loaded successfully"); } catch (Exception ex) { _loggerService.LogError($"Error loading main window: {ex.Message}"); MessageBox.Show($"An error occurred: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); } } private void RefreshButton_Click(object sender, RoutedEventArgs e) { try {// 再次使用数据和日志服务 var newData = _dataService.RefreshData(); DataDisplay.Text = newData; _loggerService.Log("Data refreshed"); } catch (Exception ex) { _loggerService.LogError($"Error refreshing data: {ex.Message}"); MessageBox.Show($"An error occurred: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error); } } } }
浙公网安备 33010602011771号