初始化没有“new list”的list属性;导致得到NullReferenceException

本文关键字:list NullReferenceException 属性 new 初始化 | 更新日期: 2023-09-27 18:10:56

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" }
         }
      };
   }
}

上面的程序可以很好地编译,但是在运行时崩溃,因为Object引用没有设置为对象的实例。

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

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

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

我的问题是为什么c#编译器在看到第一个构造时不抱怨?

为什么错误的初始化语法是有效的?

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

初始化没有“new list”的list属性;导致得到NullReferenceException

这不是语法错误,是你在一个没有实例化的属性上使用了对象初始化器。您所写的内容可以扩展到

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

抛出NullReferenceException:您试图分配属性Child所包含的属性Strings,而Child仍然是null。首先使用构造函数实例化Child,可以解决这个问题。

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

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

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>();
  }
}

你似乎误解了集合初始化式的作用。

它只是一个语法糖,将大括号中的列表转换为一系列必须在初始化的集合对象上定义的Add()方法调用。
因此,您的= { "hello", "world" }具有与

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

显然,如果没有创建集合,这将以NullReferenceException失败。

第二种语法对只读属性有效。如果您更改代码以在各自的构造函数中初始化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" }
            }
        };
    }
}

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

注意,这种语法可能会导致一些意想不到的结果和难以发现的错误:

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
相关文章: