2013-06-19 49 views
1

我正在使用MEF構建一個簡單的應用程序,以便更好地理解它,而且我正面臨一個問題。該應用程序是一個簡單的計算器,您可以創建新的操作。每個操作都是導出IOperation的類。這裏的ADD操作類:MEF注入容器

[Export(typeof(IOperation))] 
internal class Add : IOperation 
{ 
    CompositionContainer _container; 

    public string Name 
    { 
     get { return "Add"; } 
    } 

    public string Symbol 
    { 
     get { return "+"; } 
    } 

    public IOperand Compute(params IOperand[] operands) 
    { 
     IOperand result = _container.GetExportedValue<IOperand>(); 
     result.Value = operands.Sum(e => e.Value); 
     return result; 
    } 
} 

(IOperand是隻公開一個雙A接口的原因,那就是在2版本,你可以有一個像表達式「(2 + 2)×4」)

我的問題,你可以看到_container爲空,當我打Compute。這個具體類是在容器組成[ImportMany(typeof(IOperation))]時創建的。所以我的問題是:有沒有辦法告訴正在顛倒控制的容器將自己的引用傳遞給此對象?

PS:我不想讓_container成爲公共財產。

EDIT1:這裏是唯一實現IOperand至今:

[Export(typeof(IOperand))] 
public class SimpleResult : IOperand 
{ 
    private double _value; 
    public double Value 
    { 
     get 
     { 
      return _value; 
     } 
     set 
     { 
      _value = value; 
     } 
    } 
} 

這是 「主」,其中組成發生了:

public class Calculator 
{ 
    [ImportMany(typeof(IOperation))] 
    private List<IOperation> _knownOperations; 
    private List<ICalculatorButton> _buttons; 
    private CompositionContainer _container; 

    public Calculator() 
    { 
     _container = new CompositionContainer(new AssemblyCatalog(Assembly.GetExecutingAssembly())); 
     _container.SatisfyImportsOnce(this); 
     _buttons = new List<ICalculatorButton>(); 

     ICalculatorButton button; 
     for (int i = 0; i < 10; i++) 
     { 
      button = _container.GetExportedValue<IDigitButton>(); 
      button.Symbol = i.ToString(); 
      ((IDigitButton)button).Value = i; 
      _buttons.Add(button); 
     } 
     foreach (IOperation op in _knownOperations) 
     { 
      button = _container.GetExportedValue<IOperationButton>(); 
      button.Symbol = op.Symbol; 
      ((IOperationButton)button).Operation = op; 
      _buttons.Add(button); 
     } 
    } 

    public IReadOnlyList<IOperation> KnownOperations 
    { 
     get { return _knownOperations.AsReadOnly(); } 
    } 

    public IReadOnlyList<ICalculatorButton> Buttons 
    { 
     get { return _buttons.AsReadOnly(); } 
    } 

    public IOperand Calculate(IOperation operation, params IOperand[] operands) 
    { 
     IOperand result = operation.Compute(operands); 
     return result; 
    } 

    public IOperand Calculate(IOperation operation, params double[] operands) 
    { 
     List<IOperand> res = new List<IOperand>(); 
     foreach (double item in operands) 
     { 
      IOperand aux = _container.GetExportedValue<IOperand>(); 
      aux.Value = item; 
      res.Add(aux); 
     } 
     return Calculate(operation, res.ToArray()); 
    } 
} 
+1

哪裏'IOperand'實現出口?你應該可以使用'[Import]'屬性導入它,並且根本不需要引用容器,假設其他地方正在進行組合。 – Matt

+0

@Matt我編輯了這個問題並插入了IOperand的實現。問題是我不認爲我想要「導入」它...我想每次都提供一個新實例計算被調用 – Leonardo

回答

5

當你得到一個新的錘子,一切都是釘子。

特別是在使用DI和MEF時,您應該始終問自己,在哪裏使用它以及哪些地方您沒有真正獲益。 DI不能完全替代正常的實例化。在你的例子中:你想建立一個模塊計算器,在那裏你可以擴展一組操作符。這就說得通了。但是你真的需要插件能力的按鈕嗎?我會用標準方式創建它們,每個操作一個。操作數一樣,在這裏使用DI真的有意義嗎?我會懷疑,因爲我想在你的最終計劃中只會有一種類型的操作數?所以你可以讓這個類可以訪問,並在需要的地方實例化它。

當使用依賴注入時,你的'模塊'不應該引用容器。你想要的是一個很好和乾淨的問題分離,整個事情被稱爲Inversion of Control,因爲您將對象實例化的控制權從模塊傳遞到容器。如果現在模塊關心的是要求容器爲自己創建對象,那麼你並沒有真正獲得任何東西。

選項1:

你一個參考容器添加無論如何並注入它:

public Calculator() 
{ 
    _container = new CompositionContainer(new AssemblyCatalog(Assembly.GetExecutingAssembly())); 
    // This registers the instance _container as CompositionContainer 
    _container.ComposeExportedValue<CompositionContainer>(_container); 
    _container.SatisfyImportsOnce(this); 
    _buttons = new List<ICalculatorButton>(); 
... 


[ImportingConstructor] 
public Add(CompositionContainer container) 
{...} 

選項2:

可以用ref參數定義Compute方法:

public IOperand Compute(params IOperand[] operands, ref IOperand result) 
{   
    result.Value = operands.Sum(e => e.Value); 
    return result; 
} 

這表示計算方法不認爲負責實例化結果。我真的很喜歡這個版本,因爲它在語義上是有意義的,對於單元測試是非常好的,你不必引用容器。

方案3:

只是使Operand類基礎設施項目的訪問,而不容器實例化它。我沒有看到任何問題,只要你不需要擴展操作數。

還有一個備註:

爲您打造,並在Calculator類中使用的容器。同樣在這裏:您的模塊不應該知道容器。你應該在最高層建立容器並讓它組裝一切。你的計算器應該只具有它邏輯上需要的屬性,甚至不應該知道它們是由容器注入的,是在單元測試中設置的,還是僅僅來自代碼(當然除了Import/Export屬性)。

最後:

Mike Taulty on MEF (Videos)

這是真的好,我有點了解到MEF與這些影片,他建立了一個計算器,太:)

+0

我正在觀看視頻...看起來很不錯...即時視頻3到目前爲止......事情是,最終我想我已經錯過了架構我的應用程序...... – Leonardo