說起IOC,可能很多初學者不知道是用來做什么的,今天正好有點時間,就來掃掃盲,順便鞏固下自己。
IOC全稱是Inversion Of Control,意為控制反轉(這些自然百度也有),可什么是控制反轉呢?
按我現在的理解,把上端依賴的項從細節轉換為抽象,並把細節轉移到第三方,這個就叫控制反轉。
怎么理解呢?最簡單的:我們有一個接口Ianimal,就像這樣:
1
2
3
4
|
public
interface iAnimal
{
void
talk();
}
|
然后我們現在用另一個類去實現該接口:
1
2
3
4
5
6
7
|
public
class dog : iAnimal
{
public
void talk()
{
Console.Write(
"小狗說:汪汪汪!"
);
}
}
|
然后呢,往常來講,我們應該是這樣調用的:
1
2
3
|
iAnimal dog =
new
dog();
dog.talk();
Console.ReadKey();
|
運行結果應該是這樣的:
到目前為止都是正確的,相信各位小伙伴一般也是如此。
但請注意,我們在調用時是直接用dog類型new出來的,這種做法其實就已經依賴於細節了。
那么如何將這里依賴的細節修改為抽象呢?很簡單,我們只需要一個工廠類幫助我們生成實際的類即可。
但是有些小伙伴可能想,工廠內也有細節啊,難道寫個工廠就是IOC了么?
當然不是,我們還將細節交給配置文件(通過反射),這樣在功能變動時,無需修改原代碼,只需要修改配置文件即可。
工廠看上去是這樣的:
1
2
3
4
5
6
7
8
9
10
|
public
class SimpleFactory
{
public
static iAnimal CreateAnimal()
{
string
classModule = ConfigurationManager.AppSettings[ "PhoneType" ];
Assembly assembly = Assembly.Load(classModule.Split(
','
)[1]);
Type type = assembly.GetType(classModule.Split(
','
)[0]);
return
(iAnimal)Activator.CreateInstance(type);
}
}
|
而我們的配置文件自然也要寫點東西了:
1
2
3
|
<appSettings>
<add key=
"AnimalType"
value= "IOCandDI.dog,IOCandDI" />
</appSettings>
|
value內,逗號前是類的全限定名(命名空間+類名),后面是命名空間。
不會操作配置文件的小伙伴們,先添加這個引用:
然后引入System.Configuration命名空間就可以操作啦。
至於反射,直接引入System.Reflection 命名空間即可操作。反射的原理也很簡單,在這里就不再贅述。
修改過后的調用和以前的調用對比:
1
2
3
4
5
6
|
iAnimal dog =
new
dog();
dog.talk();
iAnimal obj = SimpleFactory.CreateAnimal();
obj.talk();
Console.ReadKey();
|
結果是一樣的:
這個時候,如果再多一種動物,只需要添加一個繼承iAnimal接口的類,並且修改配置文件即可。
IOC帶給我們的便利正是如此。
微軟也推出了一款IOC的插件,叫做Unity,你在NuGet程序包管理中很輕松就能找到它:
首先是下載與安裝:
然后在頁面引入Microsoft.Practices.Unity就可以操作了,基本流程如下:
1
2
3
4
|
IUnityContainer container =
new
UnityContainer(); //聲明容器
container.RegisterType<iAnimal, cat>();
//注冊類型
iAnimal obj = container.Resolve<cat>();
//完成實例
obj.talk();
|
運行效果:
了解了Unity的基礎用法,我們在來看下如何利用Unity做依賴注入(DI——dependency injection)。
說到DI,就一定要知道DI的三種方式:屬性注入、構造注入、方法注入。我這里直接改造了cat類:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public
class cat : iAnimal
{
[Dependency]
public
iColor color { get ; set ; }
public
iEat eat { get ; set ; }
public
iRun run { get ; set ; }
[InjectionConstructor]
public
cat(iEat ieat)
{
eat = ieat;
}
public
void talk()
{
Console.WriteLine(
"小貓說:喵喵喵!"
);
}
[InjectionMethod]
public
void Happy(iRun irun)
{
run = irun;
}
}
|
大家一定發現了我分別在屬性、構造、方法頭上加了特性。
實際上,Dependency、InjectionConstructor、InjectionMethod就是Unity中DI操作的標識符,它們分別對應屬性注入、構造注入、方法注入。
那么,這些特性是如何幫助程序完成DI的呢?看調用代碼:
1
2
3
4
5
6
7
8
9
10
11
12
|
IUnityContainer container =
new
UnityContainer();
container.RegisterType<iAnimal, cat>();
container.RegisterType<iColor, Color>();
container.RegisterType<iEat, Eat>();
container.RegisterType<iRun, Run>();
iAnimal obj = container.Resolve<cat>();
obj.talk();
cat myCat = (cat)obj;
Console.WriteLine(
"myCat.color是空的么?{0}"
, myCat.color ==
null
);
Console.WriteLine(
"myCat.eat是空的么?{0}"
, myCat.eat ==
null
);
Console.WriteLine(
"myCat.run是空的么?{0}"
, myCat.run ==
null
);
Console.ReadKey();
|
在Unity容器內類型注冊時,需注冊cat類內3個參數的類型,用來對應3種注入方式。
運行結果如下:
3種注入方式都成功了~添加了特性的屬性(或方法)在Unity容器執行實例時會自動尋找上面3個特性(的位置)並匹配注冊類型(完成注入)。
需要注意的是,構造注入無需聲明特性也可生效,Unity會自動尋找參數最多的構造參數進行注入(前提是被注入的參數類型一定要注冊)。
ADD:在和一位小伙伴討論時,他指出了我unity的使用方法有問題,我這里的確沒有將unity容器的細節抽出到配置文件中(因為想着是基礎使用教程就沒有加),這一方面我也懶得再寫,找到一篇介紹詳細的博文供大家參考——http://www.cnblogs.com/junchu25/archive/2012/08/10/2631455.html