【问题标题】:It seems object initializer is not the same as constructor + property assignment. How so?似乎对象初始化器与构造函数+属性赋值不同。为何如此?
【发布时间】:2018-05-02 03:33:07
【问题描述】:

考虑下面的代码:

class Data
{
    public string Name;
    public string NameWithSufix;
}

class Behaviour
{
    private Data data;
    public string Name { get { return data.Name; } private set { } }

    public Behaviour()
    {
        data = new Data()
        {
            Name = "My Name",
            NameWithSufix = Name + " Sufix",
        };
        //data = new Data();
        //data.Name = "My Name";
        //data.NameWithSufix = Name + " Sufix";
    }
}

class Program
{
    static void Main(string[] args)
    {
        Behaviour behaviour = new Behaviour();
    }
}

如果您运行此程序,它将在 Name 属性处出现 NullReferenceException 失败。 Thisthis 的答案和 Visual Studio 试图说服我对象初始化程序和对象构造函数后跟属性分配是相同的,但似乎并非如此。如果我用注释代码交换构造函数的主体,它就可以工作。似乎初始化程序在尝试分配属性之前实际上并没有运行构造函数。为什么?

【问题讨论】:

  • 这是因为属性在构造函数运行之前被初始化,并且您正在访问Name 内部的data 成员。

标签: c#


【解决方案1】:

NameWithSufix = Name 中的Name 指向data.Name,此时data 为空。对象初始化器所做的更好的表示是:

Data d = new Data();
d.Name = "My Name";
d.NameWithSufix = this.data.Name /*Name*/ + " Sufix"; // <-- see the problem here

this.data = d;

看到this.data 在对象初始化程序完成之前没有设置。

C# language specification 支持这一点,如 PetSerAl 所述。

【讨论】:

  • 能否请您指出支持您最后声明的文档? Visual Studio 建议我将构造函数+赋值缩短为初始化程序,所以我认为它们是相同的,但显然它们不是。我想知道为什么不。
  • 不确定它是否记录在某处,但您的代码清楚地表明这是实现。
  • @DreadBoy 你可以看到C# Specification,它描述了对象初始化器是如何处理的。
  • @PetSerAl,谢谢,它说初始化程序创建临时变量来保存对象,初始化后它将引用值复制到实际变量。您能否将其发布为答案,以便我接受?
  • @dre 不确定这是否有帮助,因为这与我的答案相同。
【解决方案2】:

它首先运行Behavior 构造函数。问题是Data类的初始化还没有完成,所以下面的引用抛出异常

NameWithSufix = Name + " Sufix",

因为它调用了get { return data.Name; },但此时data仍然是null


更新:

Patrick Hofman 在他的回答中更好、更准确地说明了这一点 - 并不是 Data 类的初始化尚未完成,而是新实例尚未分配给 data 变量.

另外,he requested a clarification to the official docs - 如果您同意,请点个赞。

【讨论】:

  • 它没有先运行构造函数,可能要重新写一下这个。
  • 我不相信。 data 不应该为 null,因为应该已经调用了构造函数。这就是我链接到的两个 SO 问题告诉我的。不过,我在官方文档中找不到任何关于操作顺序的信息。
  • @DreadBoy 尝试使用 .net 文档而不是 SO。构造函数不会在成员初始化之前运行。
  • @Crowcoder,你能帮我找到吗?通过阅读这个 (docs.microsoft.com/en-us/dotnet/csharp/programming-guide/…) 我发现只有“编译器通过首先访问默认实例构造函数然后处理成员初始化来处理对象初始化程序”这听起来像构造函数首先运行。当构造函数完成时,应该创建对象,对吧?
  • 我认为文档应该阅读“编译器通过首先访问默认实例构造函数然后处理成员初始化来处理对象初始化器,然后将实例分配给变量
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-08
相关资源
最近更新 更多