在上一篇关于 MVVM 的文章中我们把 View 和 Model 解耦了。但在实际开发中还有一个更大的痛点类与类之间的强耦合。如果你在写代码时每次用到别的对象就直接new一个一旦项目变大你的代码就会变成一团乱麻。为了解决这个问题现代软件开发引进了依赖注入DI和控制反转IoC。今天我用最通俗的“新能源电车”的例子带你把这两个面试必考的概念彻底搞懂。1. 什么是依赖注入DI我们先把这个词拆开依赖、注入、依赖注入。1.1 什么是依赖如果类 A 离不开类 B我们就说 A 依赖 B。比如一辆新能源电车离不开电池。那么电车类就依赖电池类。// 电池类 public class Battery { } // 汽车类 public class Car { // 痛点Car 里面直接 new 了 Battery这叫强耦合。 // 如果哪天电池要加参数Car 类也得跟着改。 private Battery _battery new Battery(); }1.2什么是注入注入通俗地说就是“把外部的东西塞进来”。我们在写构造函数时不自己new对象而是要求外部把对象当成参数传进来。public class A { public A(object B) { // 这里的 B 就是外部“注入”进来的 } } public static void Main() { object b new object(); // 把 b 注入到 A 里面 A a new A(b); }1.3现在那依赖注入的意思也呼之欲出把 1.1 和 1.2 结合起来我不在内部new电池了而是在造汽车的时候把电池通过构造函数塞进去。同时为了更彻底地解耦我们通常会引入接口。汽车不依赖具体的“某品牌电池”而是依赖“电池的通用接口”。// 1. 定义电池接口解耦具体实现 public interface IBattery { } public class BatteryA : IBattery { } public class BatteryB : IBattery { } // 2. 汽车类依赖接口不依赖具体类 public class Car { private IBattery _battery; //通过构造函数注入依赖 public Car(IBattery battery) { _battery battery; } } // 实际使用时 // Car myCar new Car(new BatteryA()); // 想用A电池就传A看到上面的代码你可能就好奇了“参数怎么凭空生成了如果每次用都要在Main里面自己手动new一个电池传进去那我不还是得自己管理这些对象吗”。实则不然。在大型项目里这背后有一个管家——DI 容器也叫 IoC 容器。我们不再手动new对象而是告诉大管家“我要造一辆车你自己看着办。”大管家接管了对象的创建权这个权力转移的过程就叫控制反转IoC, Inversion of Control。2.控制反转容器IoC Container在现代 .NET如 .NET 8中官方自带了 DI 容器。它的使用分为两步注册Register简单讲就是创建一份制造清单解析Resolve简单讲就是根据制造清单制造我们想要的东西using Microsoft.Extensions.DependencyInjection; // 需要 NuGet 引入 public static void Main() { // 1. 创建服务集合准备一个流水线 var services new ServiceCollection(); // 2. 注册告诉流水线怎么造零件 // 告诉容器如果有人要 IBattery你就给他 new 一个 BatteryA services.AddTransientIBattery, BatteryA(); services.AddTransientCar(); // 3. 解析构建管家并向管家要车 var provider services.BuildServiceProvider(); // 我们只要 Car容器会自动把 BatteryA 塞进去 var car provider.GetRequiredServiceCar(); }底层原理反射Reflection的降维打击你可能会想容器凭什么能自动推导、无需选择、顺藤摸瓜因为 .NET 容器在底层使用了反射Reflection。当你执行GetRequiredServiceCar()时容器偷偷看了Car的构造函数发现它需要一个IBattery。于是它立刻去登记表里找IBattery的注册记录把BatteryA实例化并塞进去。哪怕依赖嵌套了十层比如 Car 依赖 BatteryBattery 依赖电芯它都能一层层顺藤摸瓜自动按顺序造好。3. 容器的三大生命周期在注册时我们用了AddTransient。其实 .NET 还提供了另外两个非常重要的生命周期方法它们决定了“对象能活多久”AddTransient瞬态每次向容器要它都给你new一个新的。适合用完就扔的临时工具。AddSingleton单例整个程序从启动到关闭只new一次。以后谁要都给同一个对象。AddScoped作用域在特定范围内是单例Web 开发常用上位机很少用。注意在我的上位机项目中负责 SQLite 持久化的SqliteRepository包含了长连接和后台消费者线程。这里必须注册为AddSingleton如果用了AddTransient每次切模式都创建一个新实例你的程序瞬间会打开几十个数据库连接直接死锁崩溃。4. 应用到 WPF 中App.xaml.cs理论说完了在 WPF 项目里我们通常把这个管家建在程序的入口处public partial class App : Application { public static IServiceProvider ServiceProvider { get; private set; } protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e); var services new ServiceCollection(); // 注册长连接单例 services.AddSingletonSqliteRepository(); // 注册主界面和 VM services.AddSingletonMainViewModel(); services.AddSingletonMainWindow(); ServiceProvider services.BuildServiceProvider(); // 解析主界面并展示 var mainWindow ServiceProvider.GetRequiredServiceMainWindow(); mainWindow.Show(); } }一句话概括依赖注入就是“我不造我只用”。以前你得自己跑去买零件组装汽车现在你只需要写个清单把脏活累活丢给大管家DI 容器它自然会完美的汽车送到你手上。3.完整代码新建标签页https://gitee.com/rebel-z/simple-hmi
阅读完成 · 觉得有帮助?