<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
學習IOC之前先來了解一個依賴導致原則(DIP),依賴導致原則是IOC的核心原理。
依賴導致:即上層模組不應該依賴於低層模組,二者應該通過抽象來依賴。依賴於抽象,而不是依賴於細節。
首先來看下面的例子:
1、定義一個介面,封裝資料庫的基本CRUD操作,介面定義如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Data; namespace DataBase.Interface { /// <summary> /// 資料存取介面 /// </summary> public interface IDbInterface { string Insert(); string Delete(); string Update(); string Query(); } }
2、定義一個MSSQL類實現該介面,用來模仿SQLServer操作,MSSQL類定義如下:
using DataBase.Interface; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DataBase.MSSQL { public class DbMSSQL : IDbInterface { public string Delete() { return "MSSQL執行刪除"; } public string Insert() { return "MSSQL執行插入"; } public string Query() { return "MSSQL執行查詢"; } public string Update() { return "MSSQL執行更新"; } } }
3、定義一個Oracle類實現該介面,模仿Oracle的操作,Oracle類定義如下:
using DataBase.Interface; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DataBase.Oracle { public class DbOracle : IDbInterface { public string Delete() { return "Oracle執行刪除"; } public string Insert() { return "Oracle執行插入"; } public string Query() { return "Oracle執行查詢"; } public string Update() { return "Oracle執行更新"; } } }
4、定義一個控制檯應用程式來呼叫:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using DataBase.Interface; using DataBase.MSSQL; namespace IOCConApp { class Program { static void Main(string[] args) { // 常規做法,即程式的上端,依賴於下端,依賴於細節 DbMSSQL mssql = new DbMSSQL(); } } }
常規做法是新增參照,然後直接範例化類,但是這樣會依賴於細節實現,現將程式碼修改如下:
// 通過抽象來依賴 IDbInterface dbInterface = new DbMSSQL();
但是這樣修改以後,雖然左邊是抽象了,但是右邊還是依賴於細節。
那就究竟什麼是IOC呢?
IOC(Inversion of Control)即控制反轉,是一個重要的物件導向程式設計的法則來消減程式之間的耦合問題,把程式中上層對下層依賴,轉移到一個第三方容器中來裝配。IOC是程式設計的目標,實現方式包含依賴注入和依賴查詢,在.net中只有依賴注入。
說到IOC,就不能不說DI。DI:即依賴注入,是IOC的實現手段。
Unity是一個IoC容器,用來實現依賴注入(Dependency Injection,DI),減少耦合的,Unity出自於偉大的微軟。
unity能夠做什麼呢,列舉部分如下:
1.Unity支援簡單物件建立,特別是分層物件結構和依賴,以簡化程式程式碼。其包含一個編譯那些可能存在依賴於其他物件的物件範例機制。
2.Unity支援必要的抽象,其允許開發者在執行時或設定去指定依賴關係同時可以簡單的管理橫切點(AOP)。
3.Unity增加了推遲到容器元件設定的靈活性。其同樣支援一個容器層次的結構。
4.Unity擁有服務定位能力,對於一個程式在許多情況下重複使用元件來分離和集中功能是非常有用的。
5.Unity允許使用者端儲存或快取容器。對於在ASP.NET Web applications中開發者將容器持久化於ASP.NET中的session或application中特別有效。
6.Unity擁有攔截能力,其允許開發者通過建立並執行handlers(在方法或屬性被呼叫到達之前)來為已存在的元件增加一個函數,並再次為返回撥用結果。
7.Unity可以從標準設定系統中讀取設定資訊,例如:XML檔案,同時使用組態檔來設定容器。
8.Unity支援開發者實現自定義容器擴充套件,例如:你可以實現方法來允許額外的物件構造和容器特徵,例如快取。
9.Unity允許架構師和開發者在現代化的程式中更簡單的實現通用設計模式。
什麼情況下要使用unity呢?
1.所構建的系統依賴於健全的物件導向原則,但是大量不同的程式碼交織在一起而難以維護。
2.構建的物件和類需要依賴其他物件或類。
3.依賴於複雜的或需要抽象的物件。
4.希望利用建構函式、方法或屬性的呼叫注入優勢。
5.希望管理物件範例的生命週期。
6.希望能夠在執行時管理並改變依賴關係。
7.希望在攔截方法或屬性呼叫的時候生成一個策略鏈或管道處理容器來實現橫切(AOP)任務。
8.希望在Web Application中的回發操作時能夠快取或持久化依賴關係。
使用管理NuGet程式包來安裝Unity,在專案上右鍵,選擇管理NuGet程式包:
在搜尋方塊裡面輸入Unity,點選右側安裝按鈕進行安裝:
出現以下資訊表示安裝成功:
先來看看最簡單的Unity實現方式:
IUnityContainer container = new UnityContainer();//1、定義一個空容器 container.RegisterType<IDbInterface, DbMSSQL>();//2、註冊型別,表示遇到IDbInterface的型別,建立DbMSSQL的範例 var db = container.Resolve<IDbInterface>(); Console.WriteLine(db.Insert()); Console.ReadKey();
結果:
從結果中可以看出,db是DbMSSQL型別的範例。
除了使用RegisterType註冊型別以外,還可以註冊一個範例,例如:
// 使用RegisterInstance註冊IDbInterface的範例:new DbMSSQL() container.RegisterInstance<IDbInterface>(new DbMSSQL());
三種注入方式:建構函式注入、屬性注入、方法注入。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DataBase.Interface { public interface IHeadphone { } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DataBase.Interface { public interface IMicrophone { } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DataBase.Interface { public interface IPower { } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DataBase.Interface { public interface IPhone { void Call(); IMicrophone iMicrophone { get; set; } IHeadphone iHeadphone { get; set; } IPower iPower { get; set; } } }
IPhone介面的實現如下:
using DataBase.Interface; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Unity.Attributes; namespace DataBase.MSSQL { public class ApplePhone : IPhone { [Dependency]//屬性注入 public IMicrophone iMicrophone { get; set; } public IHeadphone iHeadphone { get; set; } public IPower iPower { get; set; } [InjectionConstructor]//建構函式注入 public ApplePhone(IHeadphone headphone) { this.iHeadphone = headphone; Console.WriteLine("{0}帶引數建構函式", this.GetType().Name); } public void Call() { Console.WriteLine("{0}打電話", this.GetType().Name); ; } [InjectionMethod]//方法注入 public void Init1234(IPower power) { this.iPower = power; } } }
IHeadphone介面的實現如下:
using DataBase.Interface; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DataBase.MSSQL { public class Headphone : IHeadphone { public Headphone() { Console.WriteLine("Headphone 被構造"); } } }
IMicrophone介面的實現如下:
using DataBase.Interface; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DataBase.MSSQL { public class Microphone : IMicrophone { public Microphone() { Console.WriteLine("Microphone 被構造"); } } }
IPower介面的實現如下:
using DataBase.Interface; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DataBase.MSSQL { public class Power : IPower { public Power() { Console.WriteLine("Power 被構造"); } } }
控制檯程式呼叫:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using DataBase.Interface; using DataBase.MSSQL; using Unity; namespace IOCConApp { /// <summary> /// IOC():控制反轉,把程式上層對下層的依賴,轉移到第三方的容器來裝配 /// 是程式設計的目標,實現方式包含了依賴注入和依賴查詢(.net裡面只有依賴注入) /// DI:依賴注入,是IOC的實習方式。 /// </summary> class Program { static void Main(string[] args) { #region MyRegion //// 常規做法,即程式的上端,依賴於下端,依賴於細節 //DbMSSQL mssql = new DbMSSQL(); //// 通過抽象來依賴 //IDbInterface dbInterface = new DbMSSQL(); //IUnityContainer container = new UnityContainer();//1、定義一個空容器 //container.RegisterType<IDbInterface, DbMSSQL>();//2、註冊型別,表示遇到IDbInterface的型別,建立DbMSSQL的範例 //var db = container.Resolve<IDbInterface>(); //// 使用RegisterInstance註冊IDbInterface的範例:new DbMSSQL() //container.RegisterInstance<IDbInterface>(new DbMSSQL()); //Console.WriteLine(db.Insert()); #endregion IUnityContainer container = new UnityContainer(); container.RegisterType<IPhone, ApplePhone>(); container.RegisterType<IMicrophone, Microphone>(); container.RegisterType<IHeadphone, Headphone>(); container.RegisterType<IPower, Power>(); IPhone phone = container.Resolve<IPhone>(); Console.WriteLine($"phone.iHeadphone==null? {phone.iHeadphone == null}"); Console.WriteLine($"phone.iMicrophone==null? {phone.iMicrophone == null}"); Console.WriteLine($"phone.iPower==null? {phone.iPower == null}"); Console.ReadKey(); } } }
輸出結果:
從輸出結果中可以看出三種注入方式的執行順序:先執行建構函式注入,在執行屬性注入,最後執行方法注入。
注意:預設情況下如果建構函式上面沒有使用特性,那麼預設找引數最多的建構函式執行注入。
如果多個不同的範例實現同一個介面,這種情況該怎麼註冊呢?先來看看下面的程式碼:
IUnityContainer container = new UnityContainer();//1、定義一個空容器 container.RegisterType<IDbInterface, DbMSSQL>();//2、註冊型別,表示遇到IDbInterface的型別,建立DbMSSQL的範例 container.RegisterType<IDbInterface, DbOracle>();//表示遇到IDbInterface的型別,建立DbMSSQL的範例 var db = container.Resolve<IDbInterface>(); Console.WriteLine(db.Insert());
執行結果:
從執行結果中可以看出,後面註冊的型別會把前面註冊的型別給覆蓋掉,那麼該如何解決呢?可以通過引數的方式來解決,程式碼如下:
IUnityContainer container = new UnityContainer();//1、定義一個空容器 container.RegisterType<IDbInterface, DbMSSQL>("sql");//2、註冊型別,表示遇到IDbInterface的型別,建立DbMSSQL的範例 container.RegisterType<IDbInterface, DbOracle>("oracle");//表示遇到IDbInterface的型別,建立DbMSSQL的範例 var sql = container.Resolve<IDbInterface>("sql"); var oracle = container.Resolve<IDbInterface>("oracle"); Console.WriteLine(sql.Insert()); Console.WriteLine(oracle.Insert());
執行結果:
生命週期及一個物件從建立到釋放中間經過的時間。先看下面的程式碼:
IUnityContainer container = new UnityContainer(); container.RegisterType<IDbInterface, DbMSSQL>(); IDbInterface db1 = container.Resolve<IDbInterface>(); IDbInterface db2 = container.Resolve<IDbInterface>(); Console.WriteLine("HashCode:"+db1.GetHashCode().ToString()); Console.WriteLine("HashCode:" + db2.GetHashCode().ToString()); Console.WriteLine(object.ReferenceEquals(db1,db2));
結果:
表明db1和db2是兩個不同的範例,即預設情況下生命週期是瞬時的,每次都是建立一個新的範例。
container.RegisterType<IDbInterface, DbMSSQL>(new TransientLifetimeManager());表示是瞬時生命週期,預設情況下即這種。
在看下面的程式碼:
IUnityContainer container = new UnityContainer(); container.RegisterType<IDbInterface, DbMSSQL>(new ContainerControlledLifetimeManager()); IDbInterface db1 = container.Resolve<IDbInterface>(); IDbInterface db2 = container.Resolve<IDbInterface>(); Console.WriteLine("HashCode:" + db1.GetHashCode().ToString()); Console.WriteLine("HashCode:" + db2.GetHashCode().ToString()); Console.WriteLine(object.ReferenceEquals(db1, db2));
結果:
上圖的結果可以看出,db1和db2是同一個範例。
container.RegisterType(new ContainerControlledLifetimeManager())
表示是容器單例,每次都是同一個範例。
在上面的例子中,所有的例子都是一直在依賴於細節,那麼怎麼解決不依賴於細節呢?答案是隻能使用組態檔,組態檔如下:
<configuration> <configSections> <section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Unity.Configuration"/> </configSections> <unity> <sectionExtension type="Microsoft.Practices.Unity.InterceptionExtension.Configuration.InterceptionConfigurationExtension, Unity.Interception.Configuration"/> <containers> <container name="testContainer"> <!--逗號前面是介面型別的完全限定名:名稱空間+介面名稱,逗號後面是DLL檔案的名稱 name解決同一個介面不同範例問題--> <register type="DataBase.Interface.IDbInterface,DataBase.Interface" mapTo="DataBase.MSSQL.DbMSSQL, DataBase.MSSQL" name="sql"/> <register type="DataBase.Interface.IDbInterface,DataBase.Interface" mapTo="DataBase.Oracle.DbOracle, DataBase.Oracle" name="oracle"/> </container> </containers> </unity> </configuration>
注意:這個一個單獨的組態檔,要把屬性裡面的複製到輸出目錄改為始終複製,那麼這個組態檔才會生成到Debug目錄裡面。
程式如下:
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, "testContainer"); IDbInterface db = container.Resolve<IDbInterface>("sql"); Console.WriteLine(db.Insert());
結果:
觀察上面的程式碼,會發現,如果改成使用組態檔的方式實現的話,程式碼裡面就不會依賴於細節了,只要一個介面型別。既然沒有細節了,那麼對專案進行如下的改造:把參照裡面對細節的參照都去掉(即去掉DataBase.MSSQL和DataBase.Oracle),然後Debug資料夾裡面沒有這兩個DLL了,但是這時需要把這兩個DLL複製到Debug目錄下面,否則程式執行的時候會找不到具體實現的型別。這樣就意味著程式架構只依賴於介面。
參照裡面只要對介面的參照了,沒有對具體實現的參照。去掉了對細節的依賴。
注意:使用組態檔實現時,必須把介面的具體實現類複製到程式目錄下面。
如果有額外新增了一種資料庫,那麼只需要修改組態檔,把新的實現類複製到程式目錄下面即可實現程式的升級。
使用組態檔的時候,需要把UnityContainer容器定義為靜態的,這樣只需要讀取一次組態檔即可。
到此這篇關於C#使用Unity實現IOC的文章就介紹到這了。希望對大家的學習有所幫助,也希望大家多多支援it145.com。
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45