網站首頁 編程語言 正文
一、什么是IOC
學習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 { ////// 數據訪問接口 /// 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
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中的回發操作時能夠緩存或持久化依賴關系。
1、程序中安裝Unity
使用管理NuGet程序包來安裝Unity,在項目上右鍵,選擇管理NuGet程序包:
在搜索框里面輸入Unity,點擊右側安裝按鈕進行安裝:
出現以下信息表示安裝成功:
2、使用Unity實現DI
先來看看最簡單的Unity實現方式:
IUnityContainer container = new UnityContainer();//1、定義一個空容器 container.RegisterType();//2、注冊類型,表示遇到IDbInterface的類型,創建DbMSSQL的實例 var db = container.Resolve (); Console.WriteLine(db.Insert()); Console.ReadKey();
結果:
從結果中可以看出,db是DbMSSQL類型的實例。
除了使用RegisterType注冊類型以外,還可以注冊一個實例,例如:
// 使用RegisterInstance注冊IDbInterface的實例:new DbMSSQL() container.RegisterInstance(new DbMSSQL());
3、三種注入方式
三種注入方式:構造函數注入、屬性注入、方法注入。
3.1 定義IHeadphone接口,代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DataBase.Interface { public interface IHeadphone { } }
3.2 定義IMicrophone接口,代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DataBase.Interface { public interface IMicrophone { } }
3.3 定義IPower接口,代碼如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace DataBase.Interface { public interface IPower { } }
3.4 定義IPhone接口,代碼如下:
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; } } }
3.5 分別實現上面定義的接口
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 { ////// IOC():控制反轉,把程序上層對下層的依賴,轉移到第三方的容器來裝配 /// 是程序設計的目標,實現方式包含了依賴注入和依賴查找(.net里面只有依賴注入) /// DI:依賴注入,是IOC的實習方式。 /// class Program { static void Main(string[] args) { #region MyRegion //// 常規做法,即程序的上端,依賴于下端,依賴于細節 //DbMSSQL mssql = new DbMSSQL(); //// 通過抽象來依賴 //IDbInterface dbInterface = new DbMSSQL(); //IUnityContainer container = new UnityContainer();//1、定義一個空容器 //container.RegisterType();//2、注冊類型,表示遇到IDbInterface的類型,創建DbMSSQL的實例 //var db = container.Resolve (); //// 使用RegisterInstance注冊IDbInterface的實例:new DbMSSQL() //container.RegisterInstance (new DbMSSQL()); //Console.WriteLine(db.Insert()); #endregion IUnityContainer container = new UnityContainer(); container.RegisterType (); container.RegisterType (); container.RegisterType (); container.RegisterType (); IPhone phone = container.Resolve (); 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(); } } }
輸出結果:
從輸出結果中可以看出三種注入方式的執行順序:先執行構造函數注入,在執行屬性注入,最后執行方法注入。
注意:默認情況下如果構造函數上面沒有使用特性,那么默認找參數最多的構造函數執行注入。
4、一個接口多個實現進行注冊
如果多個不同的實例實現同一個接口,這種情況該怎么注冊呢?先來看看下面的代碼:
IUnityContainer container = new UnityContainer();//1、定義一個空容器 container.RegisterType();//2、注冊類型,表示遇到IDbInterface的類型,創建DbMSSQL的實例 container.RegisterType ();//表示遇到IDbInterface的類型,創建DbMSSQL的實例 var db = container.Resolve (); Console.WriteLine(db.Insert());
運行結果:
從運行結果中可以看出,后面注冊的類型會把前面注冊的類型給覆蓋掉,那么該如何解決呢?可以通過參數的方式來解決,代碼如下:
IUnityContainer container = new UnityContainer();//1、定義一個空容器 container.RegisterType("sql");//2、注冊類型,表示遇到IDbInterface的類型,創建DbMSSQL的實例 container.RegisterType ("oracle");//表示遇到IDbInterface的類型,創建DbMSSQL的實例 var sql = container.Resolve ("sql"); var oracle = container.Resolve ("oracle"); Console.WriteLine(sql.Insert()); Console.WriteLine(oracle.Insert());
運行結果:
5、生命周期
生命周期及一個對象從創建到釋放中間經過的時間。先看下面的代碼:
IUnityContainer container = new UnityContainer(); container.RegisterType(); IDbInterface db1 = container.Resolve (); IDbInterface db2 = container.Resolve (); Console.WriteLine("HashCode:"+db1.GetHashCode().ToString()); Console.WriteLine("HashCode:" + db2.GetHashCode().ToString()); Console.WriteLine(object.ReferenceEquals(db1,db2));
結果:
表明db1和db2是兩個不同的實例,即默認情況下生命周期是瞬時的,每次都是創建一個新的實例。
container.RegisterType
在看下面的代碼:
IUnityContainer container = new UnityContainer(); container.RegisterType(new ContainerControlledLifetimeManager()); IDbInterface db1 = container.Resolve (); IDbInterface db2 = container.Resolve (); Console.WriteLine("HashCode:" + db1.GetHashCode().ToString()); Console.WriteLine("HashCode:" + db2.GetHashCode().ToString()); Console.WriteLine(object.ReferenceEquals(db1, db2));
結果:
上圖的結果可以看出,db1和db2是同一個實例。
container.RegisterType(new ContainerControlledLifetimeManager())
表示是容器單例,每次都是同一個實例。
6、使用配置文件實現
在上面的例子中,所有的例子都是一直在依賴于細節,那么怎么解決不依賴于細節呢?答案是只能使用配置文件,配置文件如下:
注意:這個一個單獨的配置文件,要把屬性里面的復制到輸出目錄改為始終復制,那么這個配置文件才會生成到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("sql"); Console.WriteLine(db.Insert());
結果:
觀察上面的代碼,會發現,如果改成使用配置文件的方式實現的話,代碼里面就不會依賴于細節了,只要一個接口類型。既然沒有細節了,那么對項目進行如下的改造:把引用里面對細節的引用都去掉(即去掉DataBase.MSSQL和DataBase.Oracle),然后Debug文件夾里面沒有這兩個DLL了,但是這時需要把這兩個DLL復制到Debug目錄下面,否則程序運行的時候會找不到具體實現的類型。這樣就意味著程序架構只依賴于接口。
引用里面只要對接口的引用了,沒有對具體實現的引用。去掉了對細節的依賴。
注意:使用配置文件實現時,必須把接口的具體實現類復制到程序目錄下面。
如果有額外添加了一種數據庫,那么只需要修改配置文件,把新的實現類復制到程序目錄下面即可實現程序的升級。
使用配置文件的時候,需要把UnityContainer容器定義為靜態的,這樣只需要讀取一次配置文件即可。
原文鏈接:https://www.cnblogs.com/dotnet261010/p/9054201.html
相關推薦
- 2022-12-31 Android入門之Service的使用詳解_Android
- 2022-09-04 Apache?Kafka?分區重分配的實現原理解析_Linux
- 2023-01-10 Go語言defer與return執行的先后順序詳解_Golang
- 2023-06-03 python中with的具體用法_python
- 2022-10-24 C++?String部分成員模擬實現流程詳解_C 語言
- 2022-09-20 用python實現學生信息管理系統_python
- 2022-03-15 命令行工具,VO和命令行之間的轉化工具
- 2022-09-13 Python運算符的應用超全面詳細教程_python
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支