参考博客地址:
这篇文章主要介绍.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 封装转移
///
///简单工厂+配置文件+反射///
public classObjectFactory
{public staticIPhone 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 staticIPhone 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下面配置节点:
只有抽象,没有细节,好处是可扩展。
IOC控制反转:
传统开发,上端依赖(调用/指定)下端对象,这个样子会有依赖。控制反转就是把对下端对象的依赖转移到第三方容器(工厂+配置文件+反射),能够让程序拥有更好的扩展性。
下面出现一个问题:
构造A对象,但是A依赖于B对象,那就先构造B,如果B又依赖C,再构造C。。。。。。
IDAL.IBaseDAL baseDAL = newDAL.BaseDAL();
IBLL.IBaseBll baseBll= newBLL.BaseBll(baseDAL);
IPhone phone= ObjectFactory.CreatePhone(baseBll);
现在这个问题已经暴露了,下面开始了DI,依赖注入,就能做到构造某个对象时,将依赖的对象自动初始化并注入。
IOC是目标效果,需要DI依赖注入的手段。
DI依赖注入:
三种方式注入:构造函数注入、属性注入、方法注入(按照时间顺序)
[Dependency]//属性注入
[Dependency]//属性注入
public IMicrophone iMicrophone { get; set; }
[InjectionConstructor]//构造函数注入,默认找参数最多的构造函数,方法注入不加这个特性也是可以的,可以不用特性,可以去掉对容器的依赖
[InjectionConstructor]//构造函数注入:默认找参数最多的构造函数
publicApplePhoneUpdate(IHeadphone headphone)
{this.iHeadphone =headphone;
Console.WriteLine("{0} 带参数构造函数", this.GetType().Name);
}
[InjectionMethod]//方法注入
[InjectionMethod]//方法注入
public voidInit(IPower power)
{this.iPower =power;
}
如何使用Unity容器?
1、安装Unity
2、容器三部曲:
实例化容器、注册类型、获取实例
3、项目版本和服务处的版本要一直。
下面是Iphone与AndroidPhone的定义:
public interfaceIPhone
{voidCall();
IMicrophone iMicrophone {get; set; }
IHeadphone iHeadphone {get; set; }
IPower iPower {get; set; }
}public classAndroidPhone : IPhone
{public IMicrophone iMicrophone { get; set; }public IHeadphone iHeadphone { get; set; }public IPower iPower { get; set; }//public AndroidPhone()//{//Console.WriteLine("{0}构造函数", this.GetType().Name);//}
publicAndroidPhone(AbstractPad pad, IHeadphone headphone)
{
Console.WriteLine("{0}构造函数", this.GetType().Name);
}//[ElevenInjectionConstructor]
publicAndroidPhone(AbstractPad pad)
{
Console.WriteLine("{0}构造函数", this.GetType().Name);
}publicAndroidPhone(IBaseBll baseBll)
{
Console.WriteLine("{0}构造函数", this.GetType().Name);
}public voidCall()
{
Console.WriteLine("{0}打电话", this.GetType().Name); ;
}
}
View Code
IUnityContainer container = new UnityContainer();//1 实例化容器
container.RegisterType();//2 注册类型
IPhone iphone = container.Resolve();//3 获取实例
我们来看一下Unity下面RegisterType方法里面的泛型约束:
还有一个方法:
IUnityContainer container = new UnityContainer();//1 实例化容器
container.RegisterInstance(newApplePadChild());
AbstractPad abstractPad= container.Resolve();
后遭的时候,有依赖:
下面来解决这个问题:但是要保持Unity版本一致
下面是一些注册时要依赖的类型
public interfaceIHeadphone
{
}public classHeadphone : IHeadphone
{publicHeadphone(IMicrophone microphone)
{
Console.WriteLine("Headphone 被构造");
}
}public interfaceIMicrophone
{
}public classMicrophone : IMicrophone
{publicMicrophone(IPower power)
{
Console.WriteLine("Microphone 被构造");
}
}public interfaceIPower
{
}public classPower : IPower
{publicPower(IBLL.IBaseBll baseBll)
{
Console.WriteLine("Power 被构造");
}
}public interfaceIBaseBll
{voidDoSomething();
}public classBaseBll : IBaseBll
{private IBaseDAL _baseDAL = null;public BaseBll(IBaseDAL baseDAL, intid)
{
Console.WriteLine($"{nameof(BaseBll)}被构造。。。{id}。");this._baseDAL =baseDAL;
}public voidDoSomething()
{this._baseDAL.Add();this._baseDAL.Update();this._baseDAL.Find();this._baseDAL.Delete();
}
}public interfaceIBaseDAL
{voidAdd();voidDelete();voidUpdate();voidFind();
}public classBaseDAL : IBaseDAL
{publicBaseDAL()
{
Console.WriteLine($"{nameof(BaseDAL)}被构造。。。。");
}public voidAdd()
{
Console.WriteLine($"{nameof(Add)}");
}public voidDelete()
{
Console.WriteLine($"{nameof(Delete)}");
}public voidFind()
{
Console.WriteLine($"{nameof(Find)}");
}public voidUpdate()
{
Console.WriteLine($"{nameof(Update)}");
}
}
View Code
IUnityContainer container = newUnityContainer();
container.RegisterType();
container.RegisterType();
container.RegisterType();
container.RegisterType();
container.RegisterType();
container.RegisterType();
IPhone iphone= container.Resolve();
但凡是用到了需要的类型,都要给注入进去,不然容器怎么知道类型啊
Unity里面到底是怎么实现的?下面,自己来写一个IOC
1、最最基础简陋的版本:
public interfaceILTContainer
{void RegisterType();
T Resolve();
}///
///容器--工厂///
public classLTContainer : ILTContainer
{private Dictionary LTDic = new Dictionary();public void RegisterType()
{
LTDic.Add(typeof(TFrom).FullName, typeof(TTo));
}public T Resolve()
{
Type type= LTDic[typeof(T).FullName];return(T)Activator.CreateInstance(type);
}
}
}
调用一下:
ILTContainer container = newLTContainer();
container.RegisterType();var person = container.Resolve();
2、升级一点点
public interfaceIPerson
{
}public classStudent : IPerson
{
[LTInjectionConstructor]publicStudent(Animal animal)
{
Console.WriteLine("Student被构造了...");
}
}public abstract classAnimal
{
}public classCat : Animal
{publicCat()
{
Console.WriteLine("Animal被构造了....");
}
}
}
public interfaceILTContainer
{void RegisterType();
T Resolve();
}///
///容器--工厂///
public classLTContainer : ILTContainer
{private Dictionary LTDic = new Dictionary();public void RegisterType()
{
LTDic.Add(typeof(TFrom).FullName, typeof(TTo));
}public T Resolve()
{
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 paraList = new List();foreach (var item inctor.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 objectCreateObject(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 paraList = new List();foreach (var parameter inctor.GetParameters())
{
Type paraType=parameter.ParameterType;
Type targetType= this.LTDic[paraType.FullName];object para = this.CreateObject(targetType);//递归:隐形的跳出条件,就是GetParameters结果为空,targetType拥有无参数构造函数
paraList.Add(para);
}returnActivator.CreateInstance(type, paraList.ToArray());
}//属性注入+方法注入?
}
调用一下:
ILTContainer container = newLTContainer();
ILTContainer container= newLTContainer();
container.RegisterType();
container.RegisterType();var person = container.Resolve();
3、再升级一点点:
继续找出targetType的构造,找出一个合适的构造函数,分别构造其参数,继续...递归
public interfaceILTContainer
{void RegisterType();
T Resolve();
}///
///容器--工厂///
public classLTContainer : ILTContainer
{private Dictionary LTDic = new Dictionary();public void RegisterType()
{
LTDic.Add(typeof(TFrom).FullName, typeof(TTo));
}public T Resolve()
{
Type type= LTDic[typeof(T).FullName];//继续找出targetType的构造函数,找出一个合适的构造函数,分别构造其参数//继续......递归
return (T)this.CreateObject(type);
}public objectCreateObject(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 paraList = new List();foreach (var parameter inctor.GetParameters())
{
Type paraType=parameter.ParameterType;
Type targetType= this.LTDic[paraType.FullName];object para = this.CreateObject(targetType);//递归:隐形的跳出条件,就是GetParameters结果为空,targetType拥有无参数构造函数
paraList.Add(para);
}returnActivator.CreateInstance(type, paraList.ToArray());
}//属性注入+方法注入?
}
生命管理周期:
IUnityContainer container = new UnityContainer();
默认瞬时生命周期:每次都是构造一个新的
container.RegisterType();
container.RegisterType(new TransientLifetimeManager());
全局单例:全局就只有一个该类型实例
非强制性,只有通过容器获取才是单例;项目中一般推荐容器单例而不是自己写单例
container.RegisterType(newSingletonLifetimeManager());
AbstractPad pad1= container.Resolve();
AbstractPad pad2= container.Resolve();
Console.WriteLine(object.ReferenceEquals(pad1, pad2));
线程单例:同一个线程就只有一个实例,不同线程就是不同实例
container.RegisterType(newPerThreadLifetimeManager());
AbstractPad pad1= null;
AbstractPad pad2= null;
AbstractPad pad3= null;
Action act1= new Action(() =>{
pad1= container.Resolve();
Console.WriteLine($"pad1由线程id={Thread.CurrentThread.ManagedThreadId}");
});var result1 = act1.BeginInvoke(null, null);
Action act2= new Action(() =>{
pad2= container.Resolve();
Console.WriteLine($"pad2由线程id={Thread.CurrentThread.ManagedThreadId}");
});var result2 = act2.BeginInvoke(t =>{
pad3= container.Resolve();
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 interfaceIBingleContainer
{void RegisterType(LifeTimeType lifeTimeType =LifeTimeType.Transient);
T Resolve();
}///
///容器--工厂///
public classBingleContainer : IBingleContainer
{private Dictionary BingleContainerDictionary = new Dictionary();///
///缓存起来,类型的对象实例///
private Dictionary TypeObjectDictionary = new Dictionary();///
///
///
///
///
/// 默认参数,不传递就是Transient
public void RegisterType(LifeTimeType lifeTimeType =LifeTimeType.Transient)
{
BingleContainerDictionary.Add(typeof(TFrom).FullName, newRegisterInfo()
{
TargetType= typeof(TTo),
LifeTime=lifeTimeType
});
}public T Resolve()
{
RegisterInfo info= BingleContainerDictionary[typeof(T).FullName];
Type type= BingleContainerDictionary[typeof(T).FullName].TargetType;
T result= default(T);switch(info.LifeTime)
{caseLifeTimeType.Transient:
result= (T)this.CreateObject(type);break;caseLifeTimeType.Singleton:if (this.TypeObjectDictionary.ContainsKey(type))
{
result= (T)this.TypeObjectDictionary[type];
}else{
result= (T)this.CreateObject(type);this.TypeObjectDictionary[type] =result;
}break;caseLifeTimeType.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");
}returnresult;
}private objectCreateObject(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 paraList = new List();foreach (var parameter inctor.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)
{caseLifeTimeType.Transient:
para= this.CreateObject(targetType);break;caseLifeTimeType.Singleton://需要线程安全 双if+lock
{if (this.TypeObjectDictionary.ContainsKey(targetType))
{
para= this.TypeObjectDictionary[targetType];
}else{
para= this.CreateObject(targetType);this.TypeObjectDictionary[targetType] =para;
}
}break;caseLifeTimeType.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);
}returnActivator.CreateInstance(type, paraList.ToArray());
}//属性注入+方法注入?
}
public classRegisterInfo
{///
///目标类型///
public Type TargetType { get; set; }///
///生命周期///
public LifeTimeType LifeTime { get; set; }
}public enumLifeTimeType
{
Transient,
Singleton,
PerThread
}
IBingleContainer container = newBingleContainer();
container.RegisterType(LifeTimeType.PerThread);
container.RegisterType(LifeTimeType.PerThread);
container.RegisterType(LifeTimeType.Transient);
container.RegisterType(LifeTimeType.Singleton);
container.RegisterType();
container.RegisterType();
container.RegisterType();
IPhone pad1= null;
IPhone pad2= null;
IPhone pad3= null;//pad1 = container.Resolve();
Action act1 = new Action(() =>{
pad1= container.Resolve();
Console.WriteLine($"pad1由线程id={Thread.CurrentThread.ManagedThreadId}");
});var result1 = act1.BeginInvoke(null, null);
Action act2= new Action(() =>{
pad2= container.Resolve();
Console.WriteLine($"pad2由线程id={Thread.CurrentThread.ManagedThreadId}");
});var result2 = act2.BeginInvoke(t =>{
pad3= container.Resolve();
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 = newExeConfigurationFileMap();
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= newUnityContainer();
section.Configure(container,"testContainer1");//container.AddNewExtension().Configure()//.SetInterceptorFor(new InterfaceInterceptor());
IPhone phone= container.Resolve();
phone.Call();
IPhone android= container.Resolve("Android");
android.Call();
IDBContext context = container.Resolve>();
context.DoNothing();
配置文件:
View Code