1

我正在使用 MEF 构建一个简单的应用程序以更好地理解它,但我遇到了一个问题。该应用程序是一个简单的计算器,您可以创建新的操作。每个操作都是一个导出 IOperation 的类。这是 ADD 操作类:

[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 是一个只暴露一个 double 的接口。原因是在版本 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());
    }
}
4

1 回答 1

5

当你得到一把新锤子时,一切都是钉子。

特别是在使用 DI 和 MEF 时,您应该始终问自己,在哪里使用它以及在哪里没有真正获得任何东西。DI 并没有完全取代正常的实例化。在您的示例中:您想构建一个模块化计算器,您可以在其中扩展运算符集。这就说得通了。但是你真的需要按钮的插件功能吗?我只会以标准方式创建它们,每个操作一个。操作数也一样,在这里使用 DI 真的有意义吗?我会怀疑,因为我想你的最终程序中只有一种操作数?因此,您可以使该类可访问并在您需要的任何地方实例化它。

使用依赖注入时,您的“模块”不应引用容器。你想要的是一个很好的和干净的关注点分离,整个事情被称为控制反转,因为你将对对象实例化的控制从模块传递给容器。如果现在模块关心的是要求容器为自己创建对象,那么您并没有真正获得任何东西。

选项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:

您可以Compute使用参数定义方法ref

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

这表示计算方法不负责实例化结果。我实际上喜欢这个版本,因为它在语义上很有意义,非常适合单元测试,而且您不必引用容器。

选项 3:

只需使Operand该类在基础设施项目中可访问,并在没有容器的情况下对其进行实例化。我认为这没有任何问题,只要您并不真正需要操作数的可扩展性。

再说一句:

您在类中构建和使用容器Calculator。同样在这里:您的模块不应该知道容器。您应该在最高级别构建容器并让它组装所有东西。您的计算器应该只具有它逻辑上需要的属性,甚至不应该知道它们是由容器注入、在单元测试中设置还是仅从代码中注入(当然除了 Import/Export 属性)。

最后:

MEF 上的 Mike Taulty(视频)

这真的很好,我通过这些视频学习了 MEF,他也制作了一个计算器 :)

于 2013-06-20T06:50:26.570 回答