为什么要使用ImportingConstructor

时间:2012-05-31 20:09:17

标签: .net mef

我试图了解何时[ImportingConstructor]比使用[import]装饰属性更合适。这是个人偏好,还是允许其他DI容器构建类的东西,或者[import]有什么好处?

我认为,如果您不想公开公共财产,但MEF也会解析私有字段,那么这又有什么好处呢?

4 个答案:

答案 0 :(得分:24)

使用[Import]的问题在于它将对象的创建分为两个不同且可观察的阶段:创建和初始化。其中[ImportingConstructor]允许将其保持为与其他所有.Net对象完全相同的单个阶段。

这种差异在很多方面都可以观察到

  1. 在字段上添加新的[Import]会更改类型的逻辑合约。但它并没有改变公共或使用合同。这意味着以前编译的任何代码都将继续编译,即使对象依赖项已更改(想想单元测试)。这需要编译时错误并使其成为运行时错误。
  2. 如果您拥有[Import],则代码合约将无法使用。合同验证引擎正确识别所有字段都可以作为null值存在,并且在每次使用字段之前都需要进行检查。
  3. 即使您的对象在逻辑上可以包含在初始化时设置但之后从未重置的字段,您也无法像使用普通C#对象那样使用readonly来表达。

答案 1 :(得分:9)

不要纯粹根据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的实例,则显式必须为要正确初始化的对象提供此附加服务(ILogger)。这有助于多种方式:

  1. 您表达了您的类型所需的依赖关系,这形成了必须满足的初始化合同,以确保在可用的状态中创建类型。
  2. 通过确保将服务传递给您的类型,可以降低运行时错误的风险。
  3. 您可以使用代码约定(如@JaredPar所述)在编译时进行静态检查,从而进一步改进此设计。

    就MEF而言,您可以使用[Import]代替[ImportingConstructor],因为当MEF无法满足某个类型的所有导入时,它将抛出异常,并且只会在两者之后返回该类型初始化([ImportingConstructor])然后[Import] s。

    通常建议使用构造函数注入。

答案 2 :(得分:3)

通过使用[ImportingConstructor],您允许一个用作导出的类来导入其依赖项。这大大简化了体系结构,因为您可以将具体对象的依赖性与其实现分离。

通常情况下,您会在类型上使用[ImportingConstructor],该类型本身标记为[Export]。组成类型时,构造函数参数将由MEF提供。

答案 3 :(得分:0)

构造函数参数也不支持重构。