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;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM