控制反轉(zhuǎn)(Inversion of Control钞螟,縮寫為IoC),是面向?qū)ο缶幊?/a>中的一種設(shè)計原則洞焙,可以用來減低計算機(jī)代碼之間的耦合度拯啦。其中最常見的方式叫做依賴注入(Dependency Injection褒链,簡稱DI),還有一種方式叫“依賴查找”(Dependency Lookup)甸鸟。通過控制反轉(zhuǎn)抢韭,對象在被創(chuàng)建的時候,由一個調(diào)控系統(tǒng)內(nèi)所有對象的外界實體將其所依賴的對象的引用傳遞給它瞧省。也可以說鳍贾,依賴被注入到對象中臀突。
IoC(控制反轉(zhuǎn)): 就是將原先的new對象這個操作交由第三方容器,由容器統(tǒng)一創(chuàng)建對象并管理其創(chuàng)建對象的生命周期贾漏;
DI(依賴注入): 我理解其中“依賴”有兩層意思:
- 類與類之間的依賴關(guān)系;
- 對象的創(chuàng)建依賴于容器藕筋;
"注入":不用主動從容器中獲取對象纵散,由容器根據(jù)對象依賴關(guān)系自動注入;
依賴注入:程序?qū)ο罂刂茩?quán)交給容器隐圾,統(tǒng)一依賴容器創(chuàng)建對象伍掀,類之間的依賴暇藏,也是通過容器自動注入蜜笤;(注:注入的前提是要把對象的控制權(quán)交給容器)
依賴注入優(yōu)點:
- 輕松管理類及類之間的依賴;
- 減少代碼耦合性盐碱;
- 提高代碼維護(hù)性和可擴(kuò)展性把兔;
- 由容器統(tǒng)一創(chuàng)建對象和管理生命周期;
一瓮顽、Unity 容器
1.安裝Unity依賴包
install-package unity
2.使用unity實現(xiàn)DI
定義一個接口類 ILanguage
public interface ILanguage
{
public string GetContent();
}
定義兩個實現(xiàn)類 Chinese县好,English
public class Chinese : ILanguage
{
public string GetContent()
{
return "我愛學(xué)習(xí)";
}
}
public class English : ILanguage
{
public string GetContent()
{
return "I Love Learning";
}
}
在main函數(shù)中使用unity進(jìn)行依賴管理,實現(xiàn)依賴注入
static void Main(string[] args)
{
// 容器注冊
var container = new UnityContainer();
// 依賴注入
container.RegisterType<ILanguage, Chinese>();
// 獲取依賴實例
var language = container.Resolve<ILanguage>();
//輸出內(nèi)容
Console.WriteLine(learn.GetContent());
}
執(zhí)行結(jié)果
一個接口實現(xiàn)多個注冊
static void Main(string[] args)
{
// 容器注冊
var container = new UnityContainer();
// 依賴注入
container.RegisterType<ILanguage, Chinese>();
//通過不同名稱進(jìn)行同個接口多個注冊
container.RegisterType<ILanguage, English>("english");
// 獲取依賴實例
var chinese = container.Resolve<ILanguage>();
var english = container.Resolve<ILanguage>("english");
//輸出內(nèi)容
Console.WriteLine(chinese.GetContent());
Console.WriteLine(english.GetContent());
}
執(zhí)行結(jié)果
對象的生命周期
- Transient (瞬時):默認(rèn)類型暖混,每次使用都會創(chuàng)建新的實例
- Singleton(單例):整個根容器的生命周期內(nèi)是同一個對象缕贡,只會實例化一次
- PerThread (線程作用域):在同一個線程里單例,只會被實例化一次
static void Main(string[] args)
{
// 容器注冊
var container = new UnityContainer();
//依賴注入 瞬時默認(rèn)
container.RegisterType<ILanguage, Chinese>();
var language1 = container.Resolve<ILanguage>();
var language2 = container.Resolve<ILanguage>();
Console.WriteLine($"-----------------------瞬時默認(rèn)-------------------------");
Console.WriteLine($"language1 與 language2:{ReferenceEquals(language1,language2)}\r\n");
//線程單例
Console.WriteLine($"-----------------------線程單例-------------------------");
container.RegisterType<ILanguage, Chinese>(TypeLifetime.PerThread);
ILanguage tlanguage1 = null;
var task1 = Task.Run(() => {
tlanguage1 = container.Resolve<ILanguage>();
Console.WriteLine($"tlanguage1 線程id:{Thread.CurrentThread.ManagedThreadId}");
});
ILanguage tlanguage2 = null;
ILanguage tlanguage3 = null;
var task2 = Task.Run(() => {
tlanguage2 = container.Resolve<ILanguage>();
tlanguage3 = container.Resolve<ILanguage>();
Console.WriteLine($"tlanguage2 線程id: {Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine($"tlanguage3 線程id:{Thread.CurrentThread.ManagedThreadId}");
});
task1.Wait();
task2.Wait();
Console.WriteLine($"tlanguage1 與 tlanguage2 :{ReferenceEquals(tlanguage1, tlanguage2)}");
Console.WriteLine($"tlanguage2 與 tlanguage3 :{ReferenceEquals(tlanguage2, tlanguage3)}\r\n");
//單例模式
container.RegisterType<ILanguage, Chinese>(TypeLifetime.Singleton);
var slanguage1 = container.Resolve<ILanguage>();
var slanguage2 = container.Resolve<ILanguage>();
Console.WriteLine($"-----------------------單例模式-------------------------");
Console.WriteLine($"slanguage1 與 slanguage2:{ReferenceEquals(language1, language2)}");
}
執(zhí)行結(jié)果:
二拣播、Autofac容器
AutoFac和Unity容器的用法步驟類似
創(chuàng)建容器 > 注冊接口實現(xiàn)類 > 創(chuàng)建接口容器 > 創(chuàng)建實例 > 調(diào)用實例方法
安裝依賴包
install-package autofac
main函數(shù)使用autofac管理依賴
static void Main(string[] args){
//創(chuàng)建容器
ContainerBuilder builder = new ContainerBuilder();
//依賴注入
builder.RegisterType<Chinese>().As<ILanguage>();
//創(chuàng)建接口容器
IContainer container = builder.Build();
var language = container.Resolve<ILanguage>();
//輸出內(nèi)容
Console.WriteLine(language.GetContent());
}
輸出結(jié)果:
程序集加載
注冊實現(xiàn)類多了只會注冊比較麻煩晾咪,可以使用注冊程序集統(tǒng)一注冊
static void Main(string[] args){
//創(chuàng)建容器
ContainerBuilder builder = new ContainerBuilder();
//程序集加載
Assembly assembly = Assembly.Load("IOC");// IOC程序集名稱
//依賴注入 把所有接口實現(xiàn)都注冊一遍,由此贮配,解除了程序的耦合
builder.RegisterAssemblyTypes(assembly).AsImplementedInterfaces();
//創(chuàng)建接口容器
IContainer container = builder.Build();
//獲取實例
var language = container.Resolve<ILanguage>();
//輸出內(nèi)容
Console.WriteLine(language.GetContent());
}
輸出結(jié)果:
由于程序集ILanguage結(jié)果有多個實現(xiàn)類谍倦,依賴注入默認(rèn)采用覆蓋形式,
需要一次性獲取多個實現(xiàn)類集合才能依次輸出
IEnumerable<ILanguage> languages= container.Resolve<IEnumerable<ILanguage>>();
foreach (var item in languages)
{
//輸出內(nèi)容
Console.WriteLine(item.GetContent());
}
輸出結(jié)果:
對象的生命周期
- InstancePerDependency (默認(rèn)) 每次Resolve都會創(chuàng)建新的實例
- SingleInstance (單例):整個根容器的生命周期內(nèi)是同一個對象牧嫉,只會實例化一次
- InstancePerLifetimeScope 同一個生命周期獲得一樣的對象剂跟,不同周期有不同對象(想想httpContxt)
static void Main(string[] args){
//創(chuàng)建容器
ContainerBuilder builder = new ContainerBuilder();
//依賴注入 注冊成為單例模式
builder.RegisterType<Chinese>().As<ILanguage>().SingleInstance();
//創(chuàng)建接口容器
IContainer container = builder.Build();
//獲取對象實例
var language1 = container.Resolve<ILanguage>();
var language2 = container.Resolve<ILanguage>();
//輸出內(nèi)容
Console.WriteLine($"language1 與 language2:{ReferenceEquals(language1, language2)}");
}
輸出結(jié)果: