2012-05-31 16 views
13

我想了解什麼時候[ImportingConstructor]比使用[import]裝飾屬性更合適。這是個人偏好,還是允許其他DI容器構建類的東西,還是有[import]的優點?爲什麼使用ImportingConstructor

我想如果你不想公開財產,但MEF也會解決私人領域,那麼,利益又在哪裏呢?

回答

21

使用[Import]的問題在於它將對象的創建分爲兩個截然不同的可觀察階段:創建和初始化。其中[ImportingConstructor]允許它像其他.Net對象一樣保持單一階段。

這種差異在許多方面

  1. 一個字段添加新[Import]變化的類型的邏輯合同變成可觀察的。但它不會改變公共或使用合同。這意味着即使對象依賴關係發生了變化(以爲單元測試),以前編譯的任何代碼都將繼續編譯。這需要編譯時錯誤,並使其成爲運行時錯誤。
  2. 如果您有[Import]代碼合同是不可用的。合同驗證引擎可以正確識別出所有字段都可以作爲null值存在,並且在每次使用字段之前都需要進行檢查。
  3. 儘管您的對象在邏輯上可以具有在初始化時設置的字段,並且之後永遠不會重置,但您無法像使用普通的C#對象那樣使用readonly來表示它。
+0

+1#1現在看起來如此明顯。我不知道代碼合同問題。你能再解釋一下嗎? – ILovePaperTowels

+3

@ILovePaperTowels關於#3,根據我的經驗,大部分時間你做一個'[Import]'這個字段一旦設置就永遠不會改變。在C#中,這是通過將字段'readonly'來表示的,但在'[Import]'情況下不起作用,因爲該字段在構造函數完成後被分配,這違反了'readonly'。這實際上(並且狡猾)將通過反射來工作,但是使其與非MEF組合的使用幾乎不可能 – JaredPar

2

通過使用[ImportingConstructor],您允許一個用作導出的類導入其依賴項。這極大地簡化了體系結構,因爲您可以將具體對象的依賴關係與其實現分離開來。

通常情況下,您將使用[ImportingConstructor]上的一種類型,它自身標記爲[Export]。構造類型時,構造函數參數將由MEF提供。

+0

這是爲了保證在導出解析爲導入之前所需的導入是可用的嗎? – ILovePaperTowels

+0

@ILovePaperTowels是的,如果沒有這個,你必須在導入中構造對象,使其處於無效狀態,然後重新構造它以填充依賴關係,然後調用某種形式的初始化......這非常難看。 –

8

不要單純地從MEF的角度來思考,而應從更廣義的角度來看待您的課堂設計。通常,當你在設計一個類時,你有一組相關屬性,這些可能是服務,例如,

public class MyService 
{ 
    public ILogger Logger { get; set; } 

    public void SaySomething() 
    { 
    Logger.Log("Something"); 
    } 
} 

現在,我會繼續前進,創造者的一個實例:

var service = new MyService(); 

而現在,如果我嘗試和使用方法:

service.SaySomething(); 

如果我不明確,我也必須初始化我Logger財產知道:

var service = new MyService() { Logger = new ConsoleLogger() }; 

(或):

var service = new MyService(); 
service.Logger = new ConsoleLogger(); 

然後,錯誤不會直到運行時變得顯而易見。如果我們重新定義類:

public class MyService 
{ 
    private readonly ILogger _logger; 

    public MyService(ILogger logger) 
    { 
    if (logger == null) throw new ArgumentNullException("logger"); 

    _logger = logger; 
    } 

    public void SaySomething() 
    { 
    _logger.Log("Something"); 
    } 
} 

現在,如果你嘗試創建的MyService一個實例,你必須明確提供的對象要正確初始化這個額外的服務(​​)。這有助於通過多種方式:

  1. 你表達你的類型需要的依賴關係,這形成了必須滿足,以確保在可用狀態被創建的類型的初始化合同。
  2. 您可以通過確保服務傳遞給您的類型來降低運行時錯誤的風險。

你可以通過使用Code Contracts(如@JaredPar提到的)在編譯時進行靜態檢查來改進這個設計。

在MEF而言,你可以逃脫使用[Import]代替[ImportingConstructor]作爲MEF會拋出一個異常時,它不能滿足於一個類型的所有進口,都初始化後纔會返回類型([ImportingConstructor]),然後[Import]秒。

建築師注入通常是優選的。

0

構造函數參數也不支持重構。