NetCore 依赖注入【一个New就能搞定的事情,为啥非得搞出这么多代码】
十年河东,十年河西,莫欺少年穷
学无止境,精益求精
有如下接口及继承实现类
interface Idog { public string name { get; set; } void sayhi(); } class dogTom : Idog { public string name { get; set; } public void sayhi() { Console.WriteLine("say hi from "+this.name); } }
1、New 的写法
static void Main(string[] args) { Idog idog = new dogTom(); idog.name = "汤姆"; idog.sayhi(); Console.Read(); }
2、依赖注入写法
项目引用:Microsoft.Extensions.DependencyInjection
并 using Microsoft.Extensions.DependencyInjection;
依赖注入写法:
static void Main(string[] args) { ServiceCollection services = new ServiceCollection(); services.AddScoped<Idog, dogTom>(); using (ServiceProvider provider = services.BuildServiceProvider()) { var dog = provider.GetService<Idog>(); dog.name = "tom"; dog.sayhi(); } }
两处代码比对,new更简单,那为啥微软要搞出依赖注入呢?
2.1、依赖注入提供了三种不同生命周期的对象 Singleton、Scoped、Transient

static void Main(string[] args) { ServiceCollection services = new ServiceCollection(); services.AddTransient<Idog, dogTom>(); using (ServiceProvider provider = services.BuildServiceProvider()) { var dog1 = provider.GetService<Idog>(); dog1.name = "Tom"; dog1.sayhi(); // var dog2 = provider.GetService<Idog>(); dog2.name = "汤姆"; dog2.sayhi(); var bol = object.ReferenceEquals(dog1, dog2); if (bol) { Console.WriteLine("dog 和 do2 是同一个对象"); } else { Console.WriteLine("dog 和 do2 不是同一个对象"); } } }
上述代码中的 AddTransient 改成 AddSingleton,两个对象是同一个对象么?
services.AddTransient<Idog, dogTom>();
改成
services.AddSingleton<Idog, dogTom>();

static void Main(string[] args) { ServiceCollection services = new ServiceCollection(); services.AddSingleton<Idog, dogTom>(); using (ServiceProvider provider = services.BuildServiceProvider()) { var dog1 = provider.GetService<Idog>(); dog1.name = "Tom"; dog1.sayhi(); // var dog2 = provider.GetService<Idog>(); dog2.name = "汤姆"; dog2.sayhi(); var bol = object.ReferenceEquals(dog1, dog2); if (bol) { Console.WriteLine("dog 和 do2 是同一个对象"); } else { Console.WriteLine("dog 和 do2 不是同一个对象"); } } }
由此可见,通过依赖注入实现单利模式相当简单
除了 Singleton、Transient 外,NetCore还提供了 Scoped 【一定作用范围的对象】,Scoped怎么使用呢?

static void Main(string[] args) { ServiceCollection services = new ServiceCollection(); services.AddScoped<Idog, dogTom>(); using (ServiceProvider provider = services.BuildServiceProvider()) { using (IServiceScope scope = provider.CreateScope()) { var dog = scope.ServiceProvider.GetRequiredService<Idog>(); dog.name = "tom"; dog.sayhi(); // var dog2 = scope.ServiceProvider.GetService<Idog>(); dog2.name = "汤姆"; dog2.sayhi(); var bol = object.ReferenceEquals(dog, dog2); if (bol) { Console.WriteLine("dog 和 do2 是同一个对象"); } else { Console.WriteLine("dog 和 do2 不是同一个对象"); } } } }
依赖注入除了提供了上述三种声明周期的对象外,还实现了链式依赖。
何为链式依赖?
2.2、链式依赖
看下面代码

using System; using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; namespace ConsoleApp2 { internal class Program { static void Main(string[] args) { ServiceCollection services = new ServiceCollection(); services.AddScoped<Idog, dogTom>(); services.AddScoped<Ilog, logService>(); using (ServiceProvider provider = services.BuildServiceProvider()) { using (IServiceScope scope = provider.CreateScope()) { var dog = scope.ServiceProvider.GetRequiredService<Idog>(); dog.name = "tom"; dog.sayhi(); // var dog2 = scope.ServiceProvider.GetService<Idog>(); dog2.name = "汤姆"; dog2.sayhi(); var bol = object.ReferenceEquals(dog, dog2); if (bol) { Console.WriteLine("dog 和 do2 是同一个对象"); } else { Console.WriteLine("dog 和 do2 不是同一个对象"); } } } } } interface Idog { public string name { get; set; } void sayhi(); } class dogTom : Idog { public readonly Ilog ilog; public dogTom(Ilog ilog) { this.ilog = ilog; } public string name { get; set; } public void sayhi() { ilog.logInfo(this.name + "开始自我介绍"); Console.WriteLine("say hi from "+this.name); ilog.logInfo(this.name + "自我介绍完毕"); } } interface Ilog { void logInfo(string msg); } class logService : Ilog { public void logInfo(string msg) { Console.WriteLine(msg); } } }
Program 类只创建了Idog对象,并没有创建Ilog对象,但是由于Idog对象依赖Ilog对象,因此, Program 类可以完整使用Iilog对象打印日志,这些都是框架完成的
所谓链式依赖可以理解为A依赖B,B依赖B1/B2/B3等,那么A注入B后就会顺势注入B1/B2/B3
2.3、扩展方法

using System; using System.IO; using System.Net.Http; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection; namespace ConsoleApp2 { internal class Program { static void Main(string[] args) { ServiceCollection services = new ServiceCollection(); services.AddDogService(); services.AddLogService(); using (ServiceProvider provider = services.BuildServiceProvider()) { using (IServiceScope scope = provider.CreateScope()) { var dog = scope.ServiceProvider.GetRequiredService<Idog>(); dog.name = "tom"; dog.sayhi(); // var dog2 = scope.ServiceProvider.GetService<Idog>(); dog2.name = "汤姆"; dog2.sayhi(); var bol = object.ReferenceEquals(dog, dog2); if (bol) { Console.WriteLine("dog 和 do2 是同一个对象"); } else { Console.WriteLine("dog 和 do2 不是同一个对象"); } } } } } static class ServiceExtensions { public static void AddDogService(this IServiceCollection services) { services.AddScoped<Idog, dogTom>(); } public static void AddLogService(this IServiceCollection services) { services.AddScoped<Ilog, logService>(); } } interface Idog { public string name { get; set; } void sayhi(); } class dogTom : Idog { public readonly Ilog ilog; public dogTom(Ilog ilog) { this.ilog = ilog; } public string name { get; set; } public void sayhi() { ilog.logInfo(this.name + "开始自我介绍"); Console.WriteLine("say hi from "+this.name); ilog.logInfo(this.name + "自我介绍完毕"); } } interface Ilog { void logInfo(string msg); } class logService : Ilog { public void logInfo(string msg) { Console.WriteLine(msg); } } }
上述代码中扩展方法为:
static class ServiceExtensions { public static void AddDogService(this IServiceCollection services) { services.AddScoped<Idog, dogTom>(); } public static void AddLogService(this IServiceCollection services) { services.AddScoped<Ilog, logService>(); } }
在使用时,只需调用扩展方法即可完成依赖注入
ServiceCollection services = new ServiceCollection(); services.AddDogService(); services.AddLogService();
扩展方法的好处是使代码更简洁、架构师只需写好扩展方法,程序员调用即可。
@天才卧龙的博客
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· Open-Sora 2.0 重磅开源!