.Net 内置 IOC 容器和依赖注入
1 依赖倒置
依赖倒置的核心价值:
如果没有依赖倒置,全部都是依赖细节,如果分层架构是 A层---B层--C层---D层---E层---F层,下层的修改,可能会导致上层随之改变,F层如果改变,E层要改,D层要改,C层要改......影响很大,成水波式向上影响,架构就的极度不稳定。
如果都是依赖于抽象的,抽象即接口或抽象类。 抽象是相对稳定的,修改下层不会影响上层。因为上层不是依赖于具体的,这让我们的代码更加稳定。
IOC 控制对象的创建,不就是完全控制了对象的创建吗?所以,IOC的本质是工厂。
既然 IOC 是工厂,那么工厂生产产品就是创建对象。
2 简单工厂创建对象会不会有什么问题呢?
工厂创建对象,如果对象没有无参数构造函数,必须得按照构造函数参数的要求,准备好构造函数。那就会出现问题:如果对象存在层层依赖,有多少层依赖,就得准备多少个方法。
要解决这个问题,我们就有了这样的一个目标:如果有依赖的,能够做到,在创建对象的时候,能够自动把对象依赖的对象给创建出来,然后传递进去,这样就很好。
.Net Core的内置IOC容器可以帮助我们解决掉这个问题。
3 内置IOC容器
需要引入Microsoft.Extensions.DependencyInjection
包。
Zlt.IOC.Interfaces 命名空间下创建接口:
public interface IHeadphone { } public interface IMicrophone { } public interface IPower { } public interface IPhone { void Call(); void Init123456678890(IPower iPower); IMicrophone Microphone { get; set; } IHeadphone Headphone { get; set; } IPower Power { get; set; } }
Zhaoxi.IOC.Services 实现接口:
public class Headphone : IHeadphone { public Headphone() { Console.WriteLine($"{this.GetType().Name}被构造。。"); } } public class Headphone : IHeadphone { public Headphone() { Console.WriteLine($"{GetType().Name}被构造。。"); } } public class Microphone : IMicrophone { public IHeadphone _IHeadphone { get; set; } public IHeadphone _IHeadphoneField; public void SetHeadphone(IHeadphone headphone) { _IHeadphoneField = headphone; } /// <summary> /// 没有无参数构造函数 /// </summary> /// <param name="headphone"></param> [SelectCtor] public Microphone(IHeadphone headphone) { Console.WriteLine($"{GetType().Name}被构造。。"); } public Microphone(IHeadphone headphone, IHeadphone headphone1) { Console.WriteLine($"{GetType().Name}被构造。。"); } } public class Power : IPower { public Power(IMicrophone microphone) { Console.WriteLine($"{GetType().Name}被构造。。"); } } public class AndroidPhone : IPhone { public IMicrophone Microphone { get; set; } public IHeadphone Headphone { get; set; } public IPower Power { get; set; } public AndroidPhone(IPower Power) { Power = Power; Console.WriteLine("{0}构造函数", GetType().Name); } public void Call() { Console.WriteLine("{0}打电话", GetType().Name); ; } public void Init123456678890(IPower iPower) { Power = iPower; } }
调用:
//1.IOC容器的实例 ServiceCollection services = new(); //2.注册抽象和具体之间的关系 services.AddTransient<IHeadphone, Headphone>(); services.AddTransient<IMicrophone, Microphone>(); services.AddTransient<IPower, Power>(); services.AddTransient<IPhone, AndroidPhone>(); //3.Buidl下得到一个Provider ServiceProvider serviceProvider = services.BuildServiceProvider(); //4.创建对象 IHeadphone headphone = serviceProvider.GetService<IHeadphone>(); IMicrophone microphone = serviceProvider.GetService<IMicrophone>(); IPower power = serviceProvider.GetService<IPower>(); IPhone iphone = serviceProvider.GetService<IPhone>();
这就是框架的依赖注入,如果对象A依赖于对象B,对象B依赖于对象C,在创建对象A时,自动先创建对象C,满足对象B的依赖,再创建对象B,满足A的依赖,最后创建出对象A。
4 依赖注入DI
内置容器中,仅支持构造函数注入这一种。依赖注入有3中。
- 构造函数注入:如果对象A在构造函数中依赖于对象B,对象B在构造函数中依赖于对象C,如果要构造对象A,自动构造C,传入对象B的构造函数,构造出对象B,传入对象A,构造出对象A;
- 属性注入:如果对象A在属性中依赖于对象B,对象B在属性中依赖于对象C,如果要构造对象A,在构造出对象A之后,检测属性,如果属性有依赖,就自动的去创建依赖的对象,创建出来以后,赋值给对象A中共的属性; 如果在创建的过程中,还有依赖,就继续创建
- 方法注入:如果对象A在某个方法中依赖于对象B,对象B在某个方法中依赖于对象C,如果要构造对象A,在构造出对象A之后,检测某个方法,如果某个方法的参数有依赖,就自动的去创建某个方法参数依赖的对象,创建出来以后,去执行这个方法,把构造出来的对象传递给这方法的参数; 如果在创建的过程中,还有方法依赖,就继续创建。
5 这个 IOC 和依赖注入 DI 之间,是什么关系?
IOC 是架构设计的一种方案,一种目标;
DI 是实现 IOC 容器的的时候,一种解决依赖问题的技术手段;
6 底层究竟如何实现?
下面就是关于IOC底层的构造函数注入的核心逻辑。
Zlt.IOC.Common 提供公共方法:
public interface IMyServiceCollection { /// <summary> /// 注册具体和抽象之间的关系 /// </summary> /// <typeparam name="T">抽象</typeparam> /// <typeparam name="S">具体</typeparam> public void Register<T, S>() where S : T; /// <summary> /// 获取对象的实例 /// </summary> /// <typeparam name="T"></typeparam> /// <returns></returns> public T GetService<T>(); }
public class MyServiceCollection : IMyServiceCollection { private static Dictionary<string, Type> MapDic = new Dictionary<string, Type>(); /// <summary> /// 保存 注册具体和抽象之间的关系 /// </summary> /// <typeparam name="T"></typeparam> /// <typeparam name="S"></typeparam> public void Register<T, S>() where S : T { MapDic.Add(typeof(T).FullName, typeof(S)); } /// <summary> /// 获取对象的实例 /// </summary> /// <typeparam name="T">参数:必然是抽象</typeparam> /// <returns></returns> public T GetService<T>() { string abstractFullName = typeof(T).FullName; Type type = MapDic[abstractFullName]; return (T)CreateInstance(type); } /// <summary> /// 创建对象 有依赖的递归创建 /// </summary> /// <param name="type"></param> /// <returns></returns> public object CreateInstance(Type type) { ConstructorInfo? ctor = type.GetConstructors() .Where(a => a.IsDefined(typeof(SelectCtorAttribute))) .FirstOrDefault(); if (ctor == null) { ctor = type.GetConstructors() .OrderByDescending(c => c.GetParameters().Length) .First(); } List<object> parametList = new(); foreach (var ctorParameter in ctor.GetParameters()) { Type tageType = MapDic[ctorParameter.ParameterType.FullName]; object oPrarmeter = CreateInstance(tageType); parametList.Add(oPrarmeter); } return Activator.CreateInstance(type, parametList.ToArray()); } }
[AttributeUsage(AttributeTargets.Constructor)] public class SelectCtorAttribute : Attribute { }
调用:
IMyServiceCollection myServices = new MyServiceCollection(); myServices.Register<IHeadphone, Headphone>(); myServices.Register<IMicrophone, Microphone>(); myServices.Register<IPower, Power>(); myServices.Register<IPhone, AndroidPhone>(); IHeadphone headphone1 = myServices.GetService<IHeadphone>(); IMicrophone microphone1 = myServices.GetService<IMicrophone>(); IPower power1 = myServices.GetService<IPower>(); IPhone iphone1 = myServices.GetService<IPhone>();
本文来自博客园,作者:一纸年华,转载请注明原文链接:https://www.cnblogs.com/nullcodeworld/p/18363242
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)