【问题标题】:Constructor vs Object Initializer Precedence in C#C#中的构造函数与对象初始值设定项优先级
【发布时间】:2013-06-24 00:24:24
【问题描述】:

我最近一直在学习 C# 中的对象初始化器,但现在我想知道它与构造函数冲突时它是如何工作的。

public class A
{
    public bool foo { get; set; }

    public A()
    {
        foo = true;
    }

    public A(bool bar)
    {
        foo = bar;
    }
}

当我尝试这个时会发生什么?

public class B
{
    private A a = new A() { foo = false };

    private A b = new A(true) { foo = false };
}

构造函数中的默认值是让bool 开始为真并且可以更改的好方法吗?

public A(bool bar = true)
{
    foo = bar;
}

【问题讨论】:

  • 试试你的例子。
  • 所有代码都在那里,只需点击运行
  • 是的,默认初始化器仍然有效,但如果你想要多个参数,你可以忘记的参数只是尾部参数。
  • 对,我在实际项目中尝试过使用更复杂的代码。抱歉,我猜“当我这样做时会发生什么”部分让我看起来好像只是在问这个问题。不过,我确实想要一些关于它的背景/建议。在上面找不到任何文档。

标签: c# constructor object-initializers


【解决方案1】:

基本上是保罗已经链接的内容:

来自C# 5 language specification (7.6.10.1)

包含对象初始化器或集合初始化器的对象创建表达式的处理包括首先处理实例构造器,然后处理由对象初始化器或集合初始化器指定的成员或元素初始化。

【讨论】:

    【解决方案2】:

    对象初始值设定项只是语法糖,在您编译的程序集 IL 中它们会转换为单独的语句,请在 ILSpy 上查看。

    【讨论】:

      【解决方案3】:

      构造函数首先出现,然后是对象初始化器。请记住这一点

      a = new A() { foo = false };
      

      相同
      var temp = new A();
      temp.foo = false;
      a = temp;
      

      【讨论】:

      • 他们不一样。 a = new A(); a.foo = false; 如果在为其属性赋值时抛出异常,则将对象保留一半初始化。并且使用对象成员会产生意想不到的结果(它们将使用底层类型的默认值,例如布尔成员的 false)而不是抛出 NullReferenceException。另一方面,使用 Object Initializer 或 Object 构造函数将确保如果发生异常,该对象将不会被初始化,并且如果我们尝试使用它的成员(因为它从未被初始化)将给出 NullReferenceException。
      • @WaelAlshabani 是的,创建了一个临时对象,然后在将其引用分配给变量之前将属性设置为它。是的,这是一个微妙但重要的区别,如果您的属性可能会引发异常并且您计划捕获它并出于某种原因仍然使用分配给变量的引用。但这些都不适用于这个确切的例子。但我会更新以使这一点更清楚。
      【解决方案4】:

      来自documentation

      编译器通过首先访问 默认实例构造函数,然后处理成员 初始化。

      这意味着在最简单的情况下(命名对象初始化)它基本上是调用默认构造函数然后调用属性设置器的简写(或语法糖)。在匿名类型的情况下,这种初始化实际上是必需的,而不仅仅是糖。

      对于您问题的第二部分:这更多的是样式问题,但如果您有一个关键属性,我不会创建具有默认值的构造函数。使客户端代码显式设置值。我也不确定为什么要做这样的事情:b = A(true) {foo = false}; 会是一个好主意,除非你参加了代码混淆竞赛。

      请注意:

      ...如果默认构造函数在类中声明为私有, 需要公共访问的对象初始化器将失败。

      【讨论】:

        【解决方案5】:
        b = new A(true) {foo = false};
        

        实际上是:

        A temp = new A(true);
        temp.foo = false;
        A b = temp;
        

        其中temp 是一个无法访问的变量。构造函数总是首先执行,然后是任何初始化的属性。

        【讨论】: