.NET 中的依赖注入(一):介绍

本文示例代码,均采用 .NET 6,具体的代码可以在这个仓库 Articles.DI 中获取。

让我们看这么一段简单的代码:

// https://github.com/alva-lin/Articles.DI/tree/master/WorkerService1
public class MessageWriter {
	public void Write(string message) => Console.WriteLine(message);
}

public class Worker : BackgroundService {
	private readonly MessageWriter _msgWriter = new MessageWriter();

	protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
		while (!stoppingToken.IsCancellationRequested) {
			_msgWriter.Write($"Worker running at: {DateTime.Now}");
			await Task.Delay(1000, stoppingToken);
		}
	}
}

在上面的代码中,Worker 类包含有一个 MessageWriter 类的实例成员,这个类创建并直接依赖于 MessageWriter 类。这样的代码有一些问题:

  • 如果 _msgWriter 变量要替换成别种类型的实现,需要修改 Worker 类;
  • 如果 MessageWriter 类具有其他依赖项,则必须在 Worker 类中进行配置;
  • 难以对 Worker 类进行单元测试。

如果有一个统一的管理者,负责 MessageWriter 类型的实例化,然后将其传递给有需求的类,就能做到这两个类之间的依赖解除,减少了硬编码。而这,就是依赖注入。

依赖注入和控制反转

依赖注入(英文 Dependency Injection,简称 DI),是一种在类及其依赖项之间实现控制反转(英文 Inversion of Control,简称 IoC)的技术。

控制反转,是面向对象编程中的一种设计原则,可以用来减少程序代码间的耦合程度。除了依赖注入外,还有一种实现控制反转的方法,叫做“依赖查找”。

大多数程序都由多个类相互引用,彼此依赖合作以实现业务逻辑,这使得每个对象都需要获取其他服务的引用。如果这个获取过程需要靠对象自己实现,那么这将导致类之间高度耦合,难以维护和调试。

传统应用中,如果 A 服务要获取 B 服务,则需要在自己的代码块中实例化。而如果使用依赖注入,A 服务只需声明所需的服务,由外部容器来负责生成实例,A 服务只需接收即可。整个过程从主动变成了被动,反转了对象的获取过程,即实现了控制反转

下面两张图展示了两种依赖项关系图:

直接依赖项关系图

直接依赖项关系:类 A 调用类 B 的方法,类 B 调用类 C 的方法,则在编译时,类 A 依赖与类 B,类 B 依赖与类 C。

反转依赖项关系图

应用依赖反转后,类 A 引用的是接口 IB 的方法,不再直接依赖于类 B。在编译时,则是类 A 和类 B 引用了接口 IB,和前面的直接依赖相比,反转了依赖项。而类 B 和类 C 之间也是相似的。

参考链接

依赖注入 - 维基百科

控制反转 - 维基百科

依赖关系反转 - 现代 ASP.NET Web 应用程序电子书

.NET 中的依赖关系注入

Inversion of Control Containers and the Dependency Injection pattern

依赖注入和控制反转的理解

控制反转(IoC)与依赖注入(DI)

posted @ 2022-01-17 00:51  Asjun  阅读(1238)  评论(0编辑  收藏  举报