參考博客地址: http://www.cnblogs.com/qqlin/archive/2012/10/16/2717964.html https://www.cnblogs.com/lyps/p/10560256.html 這篇文章主要介紹.NET Framework下麵的IOC以及Unity的 ...
參考博客地址:
http://www.cnblogs.com/qqlin/archive/2012/10/16/2717964.html
https://www.cnblogs.com/lyps/p/10560256.html
這篇文章主要介紹.NET Framework下麵的IOC以及Unity的使用,下一篇文章介紹.NET Core下麵自帶的容器IServiceCollection以及Autofac的使用https://www.cnblogs.com/taotaozhuanyong/p/11562184.html
IOC(Inverse of Control),控制反轉。
說到IOC,就不得不提DI(Dependency Injection),依賴註入
IOC是目標效果,需要DI依賴註入的手段。
分層架構時這些是必須的,可以劃分邊界獨立演化,也方便分工,促進代碼復用。。
依賴倒置原則DIP:
系統架構時,高層模塊不應該依賴於低層模塊,二者通過抽象來依賴。依賴抽象而不是依賴細節。在A勒種調用了B類,A類就是高層,B類就是低層。
面向抽象:
1、一個方法滿足多種類型
2、支持下層的擴展。
下麵有三種創建一個對象的方式:
AndroidPhone phone = new AndroidPhone();//1 全是細節 IPhone phone = new AndroidPhone();//2 左邊抽象右邊細節 IPhone phone = ObjectFactory.CreatePhone();//3 封裝轉移
/// <summary> /// 簡單工廠+配置文件+反射 /// </summary> public class ObjectFactory { public static IPhone CreatePhone() { string classModule = ConfigurationManager.AppSettings["iPhoneType"]; Assembly assemly = Assembly.Load(classModule.Split(',')[1]); Type type = assemly.GetType(classModule.Split(',')[0]); return (IPhone)Activator.CreateInstance(type);//無參數構造函數 } public static IPhone CreatePhone(IBaseBll iBLL) { string classModule = ConfigurationManager.AppSettings["iPhoneType"]; Assembly assemly = Assembly.Load(classModule.Split(',')[1]); Type type = assemly.GetType(classModule.Split(',')[0]); return (IPhone)Activator.CreateInstance(type, new object[] { iBLL }); } }
在App.config下麵配置節點:
<appSettings> <add key="iPhoneType" value="Bingle.Service.AndroidPhone,Bingle.Service" /> </appSettings>
只有抽象,沒有細節,好處是可擴展。
IOC控制反轉:
傳統開發,上端依賴(調用/指定)下端對象,這個樣子會有依賴。控制反轉就是把對下端對象的依賴轉移到第三方容器(工廠+配置文件+反射),能夠讓程式擁有更好的擴展性。
下麵出現一個問題:
構造A對象,但是A依賴於B對象,那就先構造B,如果B又依賴C,再構造C。。。。。。
IDAL.IBaseDAL baseDAL = new Ruamou.DAL.BaseDAL(); IBLL.IBaseBll baseBll = new Ruanmou.BLL.BaseBll(baseDAL); IPhone phone = ObjectFactory.CreatePhone(baseBll);
現在這個問題已經暴露了,下麵開始了DI,依賴註入,就能做到構造某個對象時,將依賴的對象自動初始化並註入。
IOC是目標效果,需要DI依賴註入的手段。
DI依賴註入:
三種方式註入:構造函數註入、屬性註入、方法註入(按照時間順序)
[Dependency]//屬性註入
[Dependency]//屬性註入 public IMicrophone iMicrophone { get; set; }
[InjectionConstructor]//構造函數註入,預設找參數最多的構造函數,方法註入不加這個特性也是可以的,可以不用特性,可以去掉對容器的依賴
[InjectionConstructor]//構造函數註入:預設找參數最多的構造函數 public ApplePhoneUpdate(IHeadphone headphone) { this.iHeadphone = headphone; Console.WriteLine("{0} 帶參數構造函數", this.GetType().Name); }
[InjectionMethod]//方法註入
[InjectionMethod]//方法註入 public void Init(IPower power) { this.iPower = power; }
如何使用Unity容器?
1、安裝Unity
2、容器三部曲:
實例化容器、註冊類型、獲取實例
3、項目版本和服務處的版本要一直。
下麵是Iphone與AndroidPhone的定義:
public interface IPhone { void Call(); IMicrophone iMicrophone { get; set; } IHeadphone iHeadphone { get; set; } IPower iPower { get; set; } } public class AndroidPhone : IPhone { public IMicrophone iMicrophone { get; set; } public IHeadphone iHeadphone { get; set; } public IPower iPower { get; set; } //public AndroidPhone() //{ // Console.WriteLine("{0}構造函數", this.GetType().Name); //} public AndroidPhone(AbstractPad pad, IHeadphone headphone) { Console.WriteLine("{0}構造函數", this.GetType().Name); } //[ElevenInjectionConstructor] public AndroidPhone(AbstractPad pad) { Console.WriteLine("{0}構造函數", this.GetType().Name); } public AndroidPhone(IBaseBll baseBll) { Console.WriteLine("{0}構造函數", this.GetType().Name); } public void Call() { Console.WriteLine("{0}打電話", this.GetType().Name); ; } }View Code
IUnityContainer container = new UnityContainer();//1 實例化容器 container.RegisterType<IPhone, AndroidPhone>();//2 註冊類型 IPhone iphone = container.Resolve<IPhone>();//3 獲取實例
我們來看一下Unity下麵RegisterType方法裡面的泛型約束:
還有一個方法:
IUnityContainer container = new UnityContainer();//1 實例化容器 container.RegisterInstance<AbstractPad>(new ApplePadChild()); AbstractPad abstractPad = container.Resolve<AbstractPad>();
後遭的時候,有依賴:
下麵來解決這個問題:但是要保持Unity版本一致
下麵是一些註冊時要依賴的類型
public interface IHeadphone { } public class Headphone : IHeadphone { public Headphone(IMicrophone microphone) { Console.WriteLine("Headphone 被構造"); } } public interface IMicrophone { } public class Microphone : IMicrophone { public Microphone(IPower power) { Console.WriteLine("Microphone 被構造"); } } public interface IPower { } public class Power : IPower { public Power(IBLL.IBaseBll baseBll) { Console.WriteLine("Power 被構造"); } } public interface IBaseBll { void DoSomething(); } public class BaseBll : IBaseBll { private IBaseDAL _baseDAL = null; public BaseBll(IBaseDAL baseDAL, int id) { Console.WriteLine($"{nameof(BaseBll)}被構造。。。{id}。"); this._baseDAL = baseDAL; } public void DoSomething() { this._baseDAL.Add(); this._baseDAL.Update(); this._baseDAL.Find(); this._baseDAL.Delete(); } } public interface IBaseDAL { void Add(); void Delete(); void Update(); void Find(); } public class BaseDAL : IBaseDAL { public BaseDAL() { Console.WriteLine($"{nameof(BaseDAL)}被構造。。。。"); } public void Add() { Console.WriteLine($"{nameof(Add)}"); } public void Delete() { Console.WriteLine($"{nameof(Delete)}"); } public void Find() { Console.WriteLine($"{nameof(Find)}"); } public void Update() { Console.WriteLine($"{nameof(Update)}"); } }View Code
IUnityContainer container = new UnityContainer(); container.RegisterType<IPhone, ApplePhone>(); container.RegisterType<IHeadphone, Headphone>(); container.RegisterType<IMicrophone, Microphone>(); container.RegisterType<IPower, Power>(); container.RegisterType<IBLL.IBaseBll, BLL.BaseBll>(); container.RegisterType<IDAL.IBaseDAL, Ruamou.DAL.BaseDAL>(); IPhone iphone = container.Resolve<IPhone>();
但凡是用到了需要的類型,都要給註入進去,不然容器怎麼知道類型啊
Unity裡面到底是怎麼實現的?下麵,自己來寫一個IOC
1、最最基礎簡陋的版本:
public interface ILTContainer { void RegisterType<TFrom, TTo>(); T Resolve<T>(); } /// <summary> /// 容器--工廠 /// </summary> public class LTContainer : ILTContainer { private Dictionary<string, Type> LTDic = new Dictionary<string, Type>(); public void RegisterType<TFrom, TTo>() { LTDic.Add(typeof(TFrom).FullName, typeof(TTo)); } public T Resolve<T>() { Type type = LTDic[typeof(T).FullName]; return (T)Activator.CreateInstance(type); } } }
調用一下:
ILTContainer container = new LTContainer(); container.RegisterType<IPerson, Student>(); var person = container.Resolve<IPerson>();
2、升級一點點
public interface IPerson { } public class Student : IPerson { [LTInjectionConstructor] public Student(Animal animal) { Console.WriteLine("Student被構造了..."); } } public abstract class Animal { } public class Cat : Animal { public Cat() { Console.WriteLine("Animal被構造了...."); } } }
public interface ILTContainer { void RegisterType<TFrom, TTo>(); T Resolve<T>(); } /// <summary> /// 容器--工廠 /// </summary> public class LTContainer : ILTContainer { private Dictionary<string, Type> LTDic = new Dictionary<string, Type>(); public void RegisterType<TFrom, TTo>() { LTDic.Add(typeof(TFrom).FullName, typeof(TTo)); } public T Resolve<T>() { Type type = LTDic[typeof(T).FullName]; var ctorArray = type.GetConstructors(); ConstructorInfo ctor = null; if (ctorArray.Count(c => c.IsDefined(typeof(LTInjectionConstructorAttribute), true)) > 0) { ctor = ctorArray.FirstOrDefault(c => c.IsDefined(typeof(LTInjectionConstructorAttribute), true)); } else { ctor = ctorArray.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault(); } List<object> paraList = new List<object>(); foreach (var item in ctor.GetParameters()) { Type paraType = item.ParameterType; Type targetType = this.LTDic[paraType.FullName]; paraList.Add(Activator.CreateInstance(targetType)); } return (T)Activator.CreateInstance(type, paraList.ToArray()); //return (T)this.CreateObject(type); } private object CreateObject(Type type) { ConstructorInfo[] ctorArray = type.GetConstructors(); ConstructorInfo ctor = null; if (ctorArray.Count(c => c.IsDefined(typeof(LTInjectionConstructorAttribute), true)) > 0) { ctor = ctorArray.FirstOrDefault(c => c.IsDefined(typeof(LTInjectionConstructorAttribute), true)); } else { ctor = ctorArray.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault(); } List<object> paraList = new List<object>(); foreach (var parameter in ctor.GetParameters()) { Type paraType = parameter.ParameterType; Type targetType = this.LTDic[paraType.FullName]; object para = this.CreateObject(targetType); //遞歸:隱形的跳出條件,就是GetParameters結果為空,targetType擁有無參數構造函數 paraList.Add(para); } return Activator.CreateInstance(type, paraList.ToArray()); } //屬性註入+方法註入? }
調用一下:
ILTContainer container = new LTContainer(); ILTContainer container = new LTContainer(); container.RegisterType<IPerson, Student>(); container.RegisterType<Animal, Cat>(); var person = container.Resolve<IPerson>();
3、再升級一點點:
繼續找出targetType的構造,找出一個合適的構造函數,分別構造其參數,繼續...遞歸
public interface ILTContainer { void RegisterType<TFrom, TTo>(); T Resolve<T>(); } /// <summary> /// 容器--工廠 /// </summary> public class LTContainer : ILTContainer { private Dictionary<string, Type> LTDic = new Dictionary<string, Type>(); public void RegisterType<TFrom, TTo>() { LTDic.Add(typeof(TFrom).FullName, typeof(TTo)); } public T Resolve<T>() { Type type = LTDic[typeof(T).FullName]; //繼續找出targetType的構造函數,找出一個合適的構造函數,分別構造其參數 //繼續......遞歸 return (T)this.CreateObject(type); } public object CreateObject(Type type) { ConstructorInfo[] ctorArray = type.GetConstructors(); ConstructorInfo ctor = null; if (ctorArray.Count(c => c.IsDefined(typeof(LTContainer), true)) > 0) { ctor = ctorArray.FirstOrDefault(c => c.IsDefined(typeof(LTContainer), true)); } else { ctor = ctorArray.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault(); } List<object> paraList = new List<object>(); foreach (var parameter in ctor.GetParameters()) { Type paraType = parameter.ParameterType; Type targetType = this.LTDic[paraType.FullName]; object para = this.CreateObject(targetType); //遞歸:隱形的跳出條件,就是GetParameters結果為空,targetType擁有無參數構造函數 paraList.Add(para); } return Activator.CreateInstance(type, paraList.ToArray()); } //屬性註入+方法註入? }
生命管理周期:
IUnityContainer container = new UnityContainer();
預設瞬時生命周期:每次都是構造一個新的
container.RegisterType<AbstractPad, ApplePad>(); container.RegisterType<AbstractPad, ApplePad>(new TransientLifetimeManager());
全局單例:全局就只有一個該類型實例
非強制性,只有通過容器獲取才是單例;項目中一般推薦容器單例而不是自己寫單例
container.RegisterType<AbstractPad, ApplePad>(new SingletonLifetimeManager()); AbstractPad pad1 = container.Resolve<AbstractPad>(); AbstractPad pad2 = container.Resolve<AbstractPad>(); Console.WriteLine(object.ReferenceEquals(pad1, pad2));
線程單例:同一個線程就只有一個實例,不同線程就是不同實例
container.RegisterType<AbstractPad, ApplePad>(new PerThreadLifetimeManager()); AbstractPad pad1 = null; AbstractPad pad2 = null; AbstractPad pad3 = null; Action act1 = new Action(() => { pad1 = container.Resolve<AbstractPad>(); Console.WriteLine($"pad1由線程id={Thread.CurrentThread.ManagedThreadId}"); }); var result1 = act1.BeginInvoke(null, null); Action act2 = new Action(() => { pad2 = container.Resolve<AbstractPad>(); Console.WriteLine($"pad2由線程id={Thread.CurrentThread.ManagedThreadId}"); }); var result2 = act2.BeginInvoke(t => { pad3 = container.Resolve<AbstractPad>(); Console.WriteLine($"pad3由線程id={Thread.CurrentThread.ManagedThreadId}"); Console.WriteLine($"object.ReferenceEquals(pad2, pad3)={object.ReferenceEquals(pad2, pad3)}"); }, null); act1.EndInvoke(result1); act2.EndInvoke(result2); Console.WriteLine($"object.ReferenceEquals(pad1, pad2)={object.ReferenceEquals(pad1, pad2)}");
//ExternallyControlledLifetimeManager 外部可釋放單例
//PerResolveLifetimeManager 迴圈引用
自己寫的容器裡面,加上生命周期:
public interface IBingleContainer { void RegisterType<TFrom, TTo>(LifeTimeType lifeTimeType = LifeTimeType.Transient); T Resolve<T>(); } /// <summary> /// 容器--工廠 /// </summary> public class BingleContainer : IBingleContainer { private Dictionary<string, RegisterInfo> BingleContainerDictionary = new Dictionary<string, RegisterInfo>(); /// <summary> /// 緩存起來,類型的對象實例 /// </summary> private Dictionary<Type, object> TypeObjectDictionary = new Dictionary<Type, object>(); /// <summary> /// /// </summary> /// <typeparam name="TFrom"></typeparam> /// <typeparam name="TTo"></typeparam> /// <param name="lifeTimeType">預設參數,不傳遞就是Transient</param> public void RegisterType<TFrom, TTo>(LifeTimeType lifeTimeType = LifeTimeType.Transient) { BingleContainerDictionary.Add(typeof(TFrom).FullName, new RegisterInfo() { TargetType = typeof(TTo), LifeTime = lifeTimeType }); } public T Resolve<T>() { RegisterInfo info = BingleContainerDictionary[typeof(T).FullName]; Type type = BingleContainerDictionary[typeof(T).FullName].TargetType; T result = default(T); switch (info.LifeTime) { case LifeTimeType.Transient: result = (T)this.CreateObject(type); break; case LifeTimeType.Singleton: if (this.TypeObjectDictionary.ContainsKey(type)) { result = (T)this.TypeObjectDictionary[type]; } else { result = (T)this.CreateObject(type); this.TypeObjectDictionary[type] = result; } break; case LifeTimeType.PerThread: //怎麼保證用線程校驗呢? 線程槽,把數據存在這裡 { string key = type.FullName; object oValue = CallContext.GetData(key); if (oValue == null) { result = (T)this.CreateObject(type); CallContext.SetData(key, result); } else { result = (T)oValue; } } break; default: throw new Exception("wrong LifeTime"); } return result; } private object CreateObject(Type type) { ConstructorInfo[] ctorArray = type.GetConstructors(); ConstructorInfo ctor = null; if (ctorArray.Count(c => c.IsDefined(typeof(BingleInjectionConstructorAttribute), true)) > 0) { ctor = ctorArray.FirstOrDefault(c => c.IsDefined(typeof(BingleInjectionConstructorAttribute), true)); } else { ctor = ctorArray.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault(); } List<object> paraList = new List<object>(); foreach (var parameter in ctor.GetParameters()) { Type paraType = parameter.ParameterType; RegisterInfo info = BingleContainerDictionary[paraType.FullName]; Type targetType = info.TargetType; //object para = this.CreateObject(targetType); object para = null; #region { switch (info.LifeTime) { case LifeTimeType.Transient: para = this.CreateObject(targetType); break; case LifeTimeType.Singleton: //需要線程安全 雙if+lock { if (this.TypeObjectDictionary.ContainsKey(targetType)) { para = this.TypeObjectDictionary[targetType]; } else { para = this.CreateObject(targetType); this.TypeObjectDictionary[targetType] = para; } } break; case LifeTimeType.PerThread: //怎麼保證用線程校驗呢? 線程槽,把數據存在這裡 { string key = targetType.FullName; object oValue = CallContext.GetData(key); if (oValue == null) { para = this.CreateObject(targetType); CallContext.SetData(key, para); } else { para = oValue; } } break; default: throw new Exception("wrong LifeTime"); } } #endregion //遞歸:隱形的跳出條件,就是GetParameters結果為空,targetType擁有無參數構造函數 paraList.Add(para); } return Activator.CreateInstance(type, paraList.ToArray()); } //屬性註入+方法註入? }
public class RegisterInfo { /// <summary> /// 目標類型 /// </summary> public Type TargetType { get; set; } /// <summary> /// 生命周期 /// </summary> public LifeTimeType LifeTime { get; set; } } public enum LifeTimeType { Transient, Singleton, PerThread }
IBingleContainer container = new BingleContainer(); container.RegisterType<IPhone, AndroidPhone>(LifeTimeType.PerThread); container.RegisterType<AbstractPad, ApplePad>(LifeTimeType.PerThread); container.RegisterType<IHeadphone, Headphone>(LifeTimeType.Transient); container.RegisterType<IMicrophone, Microphone>(LifeTimeType.Singleton); container.RegisterType<IPower, Power>(); container.RegisterType<IBLL.IBaseBll, BLL.BaseBll>(); container.RegisterType<IDAL.IBaseDAL, Ruamou.DAL.BaseDAL>(); IPhone pad1 = null; IPhone pad2 = null; IPhone pad3 = null; //pad1 = container.Resolve<IPhone>(); Action act1 = new Action(() => { pad1 = container.Resolve<IPhone>(); Console.WriteLine($"pad1由線程id={Thread.CurrentThread.ManagedThreadId}"); }); var result1 = act1.BeginInvoke(null, null); Action act2 = new Action(() => { pad2 = container.Resolve<IPhone>(); Console.WriteLine($"pad2由線程id={Thread.CurrentThread.ManagedThreadId}"); }); var result2 = act2.BeginInvoke(t => { pad3 = container.Resolve<IPhone>(); Console.WriteLine($"pad3由線程id={Thread.CurrentThread.ManagedThreadId}"); Console.WriteLine($"object.ReferenceEquals(pad2, pad3)={object.ReferenceEquals(pad2, pad3)}"); }, null); act1.EndInvoke(result1); act2.EndInvoke(result2); Console.WriteLine($"object.ReferenceEquals(pad1, pad2)={object.ReferenceEquals(pad1, pad2)}");
容器依賴細節?如果不想依賴細節,又想創建對象,反射+配置文件:
ExeConfigurationFileMap fileMap = new ExeConfigurationFileMap(); fileMap.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory + "CfgFiles\\Unity.Config");//找配置文件的路徑 Configuration configuration = ConfigurationManager.OpenMappedExeConfiguration(fileMap, ConfigurationUserLevel.None); UnityConfigurationSection section = (UnityConfigurationSection)configuration.GetSection(UnityConfigurationSection.SectionName); IUnityContainer container = new UnityContainer(); section.Configure(container, "testContainer1"); // container.AddNewExtension<Interception>().Configure<Interception>() //.SetInterceptorFor<IPhone>(new InterfaceInterceptor()); IPhone phone = container.Resolve<IPhone>(); phone.Call(); IPhone android = container.Resolve<IPhone>("Android"); android.Call(); IDBContext<Program> context = container.Resolve<IDBContext<Program>>(); context.DoNothing();
配置文件:
<unity> <!--<sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Microsoft.Practices.Unity.Interception.Configuration"/>--> <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configur