在没有 "new List" 的情况下初始化列表 属性 会导致 NullReferenceException

Initializing list property without "new List" causes NullReferenceException

using System;
using System.Collections.Generic;

class Parent
{
   public Child Child { get; set; }
}

class Child
{
   public List<string> Strings { get; set; }
}

static class Program
{
   static void Main() {
      // bad object initialization
      var parent = new Parent() {
         Child = {
            Strings = { "hello", "world" }
         }
      };
   }
}

以上程序编译正常,但在运行时崩溃,对象引用未设置为对象的实例

如果您在上面的代码片段中注意到,我在初始化子属性时省略了新的

显然正确的初始化方式是:

      var parent = new Parent() {
         Child = new Child() {
            Strings = new List<string> { "hello", "world" }
         }
      };

我的问题是为什么 C# 编译器在看到第一个构造时不报错?

为什么损坏的初始化有效语法?

      var parent = new Parent() {
         Child = {
            Strings = { "hello", "world" }
         }
      };

这不是语法错误,是你在 属性 上使用了对象初始值设定项,根本没有实例化。你写的可以扩展到

var parent = new Parent();
parent.Child.Strings = new List<string> { "hello", "world" };

抛出 NullReferenceException:您正在尝试分配 属性 Child 包含的 属性 StringsChild还是null。 首先使用构造函数实例化 Child,负责处理此问题。

第二种语法对只读属性有效。如果您更改代码以在各自的构造函数中初始化 Child 和 Strings 属性,则语法有效。

class Parent
{
    public Parent()
    {
        Child = new Child();
    }

    public Child Child { get; private set; }
}

class Child
{
    public Child()
    {
        Strings = new List<string>();
    }
    public List<string> Strings { get; private set; }
}

static class Program
{
    static void Main()
    {
        // works fine now
        var parent = new Parent
        {
            Child =
            {
                Strings = { "hello", "world" }
            }
        };

    }
}

初始化没有问题,但它正在尝试初始化不存在的对象。

如果 类 具有创建对象的构造函数,则初始化有效:

class Parent {
  public Child Child { get; set; }
  public Parent() {
    Child = new Child();
  }
}

class Child {
  public List<string> Strings { get; set; }
  public Child() {
    Strings = new List<string>();
  }
}

引用 null 不能总是在编译时检查。尽管编译器有时会在变量被赋值之前警告使用它。编译器工作正常。这是 运行 次错误。

您似乎误解了集合初始化程序的作用。

它只是一个语法糖,将大括号中的列表转换为必须在正在初始化的集合对象上定义的series of calls to Add() method
因此,您的 = { "hello", "world" }

具有相同的效果
.Add("hello");
.Add("world");

显然,如果未创建集合,这将失败并出现 NullReferenceException。

请注意,此语法可能会导致一些意外结果和难以发现的错误:

class Test
{
    public List<int> Ids { get; set; } = new List<int> { 1, 2 };
}

var test = new Test { Ids = { 1, 3 } };

foreach (var n in test)
{
    Console.WriteLine(n);
}

您可能希望输出为 1,3,但实际上是:

1
2
1
3