构造函数注入Constructor Injection)是依赖注入DI)的一种形式,是指通过类的构造函数将依赖项(通常是其他类或接口的实例)传递给类的实例。

这种方式可以让类在实例化时就具备所需的依赖项,使得这些依赖项对于类的实现来说是透明的,从而使得类更易于测试和维护。

示例

using System;
using Microsoft.Extensions.DependencyInjection;

namespace DI会传染
{
    internal class Program
    {
        static void Main(string[] args)
        {
            ServiceCollection services = new ServiceCollection();
            services.AddScoped<Controller>();

            services.AddScoped<ILog,LogImpl>();
            services.AddScoped<IStorage, StorageImpl>();
            //services.AddScoped<IConfig, ConfigImpl>();

            services.AddScoped<IConfig, DBConfigImpl>();

            using (var sp = services.BuildServiceProvider())
            {
                var c=sp.GetRequiredService<Controller>();

                c.Test();
            }
        }
    }

    class Controller
    {
        private readonly ILog log;
        private readonly IStorage storage;

        public Controller(ILog log, IStorage storage)
        {
            this.log = log;
            this.storage = storage;
        }

        public void Test()
        {
            log.Log("开始上传");
            storage.Save("aasdfsdfasf", "1.txt");
            log.Log("上传完毕");
        }
    }

    interface ILog
    {
        void Log(string msg);
    }

    class LogImpl : ILog
    {
        public void Log(string msg)
        {
            Console.WriteLine($"日志: {msg}");
        }
    }

    interface IConfig
    {
        public string GetValue(string name);
    }

    class ConfigImpl : IConfig
    {
        public string GetValue(string name)
        {
            return "hello";
        }
    }

    class DBConfigImpl : IConfig
    {
        public string GetValue(string name)
        {
            Console.WriteLine("从数据库读的配置");
            return "hello db";
        }
    }

    interface IStorage
    {
        public void Save(string content, string name);
    }

    class StorageImpl : IStorage
    {
        private readonly IConfig config;

        public StorageImpl(IConfig config)
        {
            this.config = config;
        }

        public void Save(string content, string name)
        {
            string server = config.GetValue("server");
            Console.WriteLine($"向服务器{server}的文件名为{name}上传{content}");
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.
  • 54.
  • 55.
  • 56.
  • 57.
  • 58.
  • 59.
  • 60.
  • 61.
  • 62.
  • 63.
  • 64.
  • 65.
  • 66.
  • 67.
  • 68.
  • 69.
  • 70.
  • 71.
  • 72.
  • 73.
  • 74.
  • 75.
  • 76.
  • 77.
  • 78.
  • 79.
  • 80.
  • 81.
  • 82.
  • 83.
  • 84.
  • 85.
  • 86.
  • 87.
  • 88.
  • 89.
  • 90.
  • 91.
  • 92.
  • 93.
  • 94.
  • 95.
  • 96.
  • 97.
  • 98.
  • 99.
  • 100.
  • 101.
  • 102.
构造函数注入相对于正常创建对象方式的一些优势:
  1. 解耦性:
  • 构造函数注入可以帮助实现依赖倒置原则,从而减少类与其依赖之间的耦合度。
  • 类的实例化过程将依赖项注入进来,不需要类自己负责实例化依赖项,使得类更加独立和可复用。
  1. 可测试性:
  • 使用构造函数注入,可以轻松地传入模拟对象或桩(stub)来进行单元测试,而不需要依赖于外部资源或具体实现。
  • 这样可以更方便地编写单元测试,提高代码的质量和稳定性。
  1. 灵活性:
  • 通过构造函数注入,可以在类的实例化时动态传入不同的依赖项,而不需要修改类的代码。
  • 这样可以实现更好的灵活性和可扩展性,方便应对未来需求的变化。
  1. 代码可读性:
  • 构造函数明确地声明了类依赖的内容,使得代码更加清晰、易于理解。
  • 开发人员可以直观地看到类依赖的实例是如何被传入的,提高了代码的可读性和可维护性。
  1. 依赖注入框架支持:
  • 许多依赖注入框架(如.NET Core中的内置依赖注入容器)支持构造函数注入,可以自动解析和注入依赖项。
  • 这简化了依赖注入的配置和管理,减少了手动编写依赖注入代码的工作量。