IOC控制反转
参考博客地址:
这篇文章主要介绍.net framework下面的ioc以及unity的使用,下一篇文章介绍.net core下面自带的容器iservicecollection以及autofac的使用
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); ; } }
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)}"); } }
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.configuration"/> <containers> <container name="testcontainer1"> <extension type="interception"/> <register type="bingle.interface.iphone,bingle.interface" mapto="bingle.service.applephone, bingle.service"/> <!--是dll名称,不是命名空间--> <register type="bingle.interface.iphone,bingle.interface" mapto="bingle.service.androidphone, bingle.service" name="android"> <!--别名--> <interceptor type="interfaceinterceptor"/> <interceptionbehavior type="bingle.framework.aop.logbeforebehavior, bingle.framework"/> <interceptionbehavior type="bingle.framework.aop.logafterbehavior, bingle.framework"/> <interceptionbehavior type="bingle.framework.aop.parametercheckbehavior, bingle.framework"/> <lifetime type="transient" /> </register> <register type="bingle.interface.imicrophone, bingle.interface" mapto="bingle.service.microphone, bingle.service"/> <register type="bingle.interface.iheadphone, bingle.interface" mapto="bingle.service.headphone, bingle.service"/> <register type="bingle.interface.ipower, bingle.interface" mapto="bingle.service.power, bingle.service"/> <register type="bingle.interface.abstractpad, bingle.interface" mapto="bingle.service.applepad, bingle.service"/> <register type="bingle.ibll.ibasebll, bingle.ibll" mapto="bingle.bll.basebll, bingle.bll"> <constructor> <param name="basedal" type="bingle.idal.ibasedal, bingle.idal" /> <param name="id" type="system.int32" value="3" /> </constructor> </register> <register type="bingle.idal.ibasedal, bingle.idal" mapto="ruamou.dal.basedal, ruamou.dal"/> <register type="bingle.idal.idbcontext`1, bingle.idal" mapto="ruamou.dal.dbcontextdal`1, ruamou.dal"/> </container> <unity>
上一篇: 孕妇可以吃苦瓜吗?知道这些不亏!
推荐阅读
-
C# 改变控制台背景颜色
-
ContentType控制输出的类型是否区分大小写
-
CodeIgniter自定义控制器MY_Controller用法分析
-
mysql 开发基础系列20 事务控制和锁定语句(上)
-
当页面引入百度地图时,谷歌浏览器的控制台会弹出一个警告信息
-
使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)
-
Java微服务:dubbo-admin控制台是使用
-
asp.net core 使用 AccessControlHelper 控制访问权限
-
win10系统打开文件总是提示请在默认程序控制面板创建关联的两种解决方法
-
Asp.net Core MVC中怎么把二级域名绑定到特定的控制器上