C# 依赖注入

c# .NET 支持依赖注入 (DI) 软件设计模式,这是一种在类及其依赖项之间实现控制反转 (IoC Inversion of Control) 的技术. 依赖注入以及IConfiguration、ILogger和Option模式是.Net世界一等公民。 (C#:是基于.NET框架上的编程语言之一,所以如果我们用Vb.net是可以用这个依赖注入的 )

依赖项

我们的类正常都不是孤立存在的。很多都是要依赖于其它的类。 比如说我们有一个Woker类,Work类在工作的时候需要把信息记录下来。 MessageWriter就是 Worker的依赖项。 代码如下

public class MessageWriter
{
    public void Write(string message)
    {
        Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
    }
}

public class Worker
{
    private readonly MessageWriter _messageWriter = new MessageWriter();

    public async Task ExecuteAsync()
    {
        _messageWriter.Write($"Worker running at: {DateTimeOffset.Now}");
    }
}

如上的代码,Worker直接创建了一个MessageWriter对象。这个叫依赖于具体实现。 这样做有下面的一些缺点。

  • 如果 MessageWriter 我们需要新加一个构造参数的话,我们需要修改所有有引用到它的地方。这样这个类的配置代码将分散到整个项目当中。
  • 这种实现会导致很难进行单元测试,因为我们必须得考虑到这个具体的方法。也不好来判断这个方法是不是有被调用了,或者对它进行Mock。
  • 要用不同的实现替换 MessageWriter,必须修改 Worker 类。

依赖关系注入是如何解决问题

依赖关系注入通过以下方式解决了这些问题:

  • 使用接口或基类将依赖项进行抽象化。(这个叫依赖于抽象)
  • 在服务容器中注册依赖关系。 .NET 提供了一个内置的服务容器 IServiceProvider。 服务通常在应用启动时注册,并追加到 IServiceCollection。 添加所有服务后,可以使用 BuildServiceProvider 创建服务容器。
  • 将服务注入到使用它的类的构造函数中。 框架负责创建依赖关系的实例,并在不再需要时将其释放。

例子

//IMessageWriter.cs
namespace Malema.net.Example
{
    public interface IMessageWriter
    {
        void Write(string message);
    }
}

//MessageWriter.cs
using System;

namespace Malema.net.Example
{
    public class MessageWriter : IMessageWriter
    {
        public void Write(string message)
        {
            Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
        }
    }
}

//Worker.cs
using System;
using System.Threading.Tasks;

namespace Malema.net.Example
{
    public class Worker
    {
        private readonly IMessageWriter _messageWriter;
        public Worker(IMessageWriter messageWriter)
        {
            _messageWriter = messageWriter;
        }

        public async Task ExecuteAsync()
        {
            _messageWriter.Write($"Worker running at: {DateTimeOffset.Now}");
        }
    }
}

上面的代码中我们抽象出一个接口IMessageWriter,然后有一个类MessageWriter实现了这个接口,在Worker类里面。 我们并没有初始化IMessageWriter。而是在Worker的构造方法通过参数 messageWriter来给它赋值。 这样如果这个MessageWriter的构造函数变了,或者我们要用其它的实现 我们都不需要去修改Worker这个类。

我们的依赖注入框架,会跟据我们的配置把合适的对象传递给Worker的构造方法。这个就被称为依赖注入。 (有的框架还允许属性注入)

其它的依赖注入框架

Autofac https://autofac.readthedocs.io/en/latest/getting-started/index.html Plugin 动态加载 MEF https://learn.microsoft.com/en-us/dotnet/framework/mef/

最近更新的
...