1. 程式人生 > >C#依賴註入控制反轉IOC實現詳解

C#依賴註入控制反轉IOC實現詳解

section 直接 pub xmlns 軟件 image 方法 問題: some

原文:C#依賴註入控制反轉IOC實現詳解

IOC的基本概念是:不創建對象,但是描述創建它們的方式。在代碼中不直接與對象和服務連接,但在配置文件中描述哪一個組件需要哪一項服務。容器負責將這些聯系在一起。

舉個例子,組件A中有類ClassA,組件B中有接口IB和其對應的實現類B1和B2。

那麽,現在ClassA需要利用IB接口來做一些事情,例如:

public class ClassA {
public void DoSomething() {
IB b = ???
b.DoWork();
}
}

現在的問題來了,IB b = ??? 中這三個???要寫什麽代碼?是要寫成 IB b = new B1(),還是要寫成IB b = new B2() ?

不管是哪一種,都會讓ClassA強依賴於IB的實現。

在上面這種方案中,ClassA通過new一個B1或B2來實現對IB的依賴的獲取,換句話說,ClassA在主動獲取依賴。

這樣的設計會讓ClassA很難擴展,那我們要改良設計:使用依賴註入。上面說到了,問題出在new這裏,也就是依賴是Class去主動獲取的,那我們就要解決這個問題:不要去主動獲取對IB的依賴(通過new),而讓這個依賴從ClassA的外面“註入”進來。註入有多種方式,比較常用的一種是通過構造函數註入,那麽,我們要把ClassA改成:

public class ClassA {
private IB b;

public ClassA(IB b) {
this.b = b;
}

public DoSomething() {
this.b.DoWork();
}
}

可以看到,通過把IB這個依賴從構造函數中“註”進來後,ClassA就不依賴IB的實現了。還可以發現,這個重構過程中,我們是把"ClassA主動獲取對IB的依賴”變成“把對IB的依賴從外部註入到ClassA中”,依賴的方向反轉了,所以,依賴註入又稱“控制反轉”。

IoC框架(如Unity, Autofac,Spring.Net),其中Unity是微軟自己封裝的,另外可以利用Extnesions.Dependency動態生成類(參考之前的將RFCTable轉為List<T>利用依賴註入動態生成類的例子),參考之前的代碼

下面以Unity作為介紹:

Unity是一個輕量級的可擴展的依賴註入容器,支持構造函數,屬性和方法調用註入。Unity可以處理那些從事基於組件的軟件工程的開發人員所面對的問題。構建一個成功應用程序的關鍵是實現非常松散的耦合設計。下面介紹一下c#中使用unity的方法(我是以webapi項目為例,但本例中並沒有針對webapi做特殊處理)

新建一個mvc4 webapi項目,下面的例子只用到get方法

技術分享圖片

技術分享圖片

用nuget安裝unity,如圖

技術分享圖片

新建一個接口類,以及繼承該接口的兩個類

技術分享圖片

直接在api/values的get中嘗試簡單實現unity

using (IUnityContainer container = new UnityContainer())

{

container.RegisterType<IBook, BBook>();

IBook a = container.Resolve<IBook>();

var strResult = a.Write();

return strResult;

}

然後在瀏覽器中查看,頁面顯示的返回值,是BBook的

如果container.RegisterType<IBook, BBook>();中的BBook改為ABook,返回值就是ABook的內容

技術分享圖片

config中代碼如下

在configSections中加入

<section name="unity"

type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration" />

在configuration中加入

<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">

<container>

<register type="testunity.Models.IBook,testunity" mapTo="testunity.Models.ABook, testunity" />

</container>

</unity>

註意 type="testunity.Models.IBook,testunity" mapTo="testunity.Models.ABook, testunity"

testunity.Models.IBook是命名空間加類名

testunity是程序集的名稱

技術分享圖片

然後cs的代碼改成

using (IUnityContainer container = new UnityContainer())

{

UnityConfigurationSection configuration = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");

configuration.Configure(container);

IBook a = container.Resolve<IBook>();

var strResult = a.Write();

return strResult;

}

在瀏覽器中可以看到返回結果對應的是config中register的那個類

技術分享圖片

每次調用都要寫IUnityContainer container = new UnityContainer()顯然不是好辦法

那就把container封裝到一個單列類中

簡單實現如下

public class ServiceLocator:IServiceProvider

{

private readonly IUnityContainer _container;

private static readonly ServiceLocator instance = new ServiceLocator();

private ServiceLocator()

{

UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");

_container = new UnityContainer();

section.Configure(_container);

}

public static ServiceLocator Instance

{

get { return instance; }

}

public object GetService(Type serviceType)

{

return _container.Resolve(serviceType);

}

public T GetService<T>()

{

return _container.Resolve<T>();

}

}

cs代碼修改如下

IBook a = ServiceLocator.Instance.GetService<IBook>();

var strResult = a.Write();

return strResult;

技術分享圖片

技術分享圖片

C#依賴註入控制反轉IOC實現詳解