Initializing list property without "new List" causes NullReferenceException

时间:2015-09-01 23:01:33

标签: c# 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" }
         }
      };
   }
}

The above program compiles fine, but crashes at runtime with Object reference not set to an instance of the object.

If you notice in the above snippet, I have omitted new while initializing the child properties.

Obviously the correct way to initialize is:

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

My question is why does the C# compiler not complain when it sees the first construct?

Why is the broken initialization valid syntax?

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

6 个答案:

答案 0 :(得分:17)

没有破坏语法,你是在一个根本没有实例化的属性上使用对象初始值设定项的人。你写的内容可以扩展到

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

这会引发NullReferenceException:当Strings仍为Child时,您尝试分配属性Child所包含的属性null。 首先使用构造函数实例化Child来处理这个问题。

答案 1 :(得分:10)

初始化没有任何问题,但是它试图初始化那些不存在的对象。

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

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

答案 2 :(得分:4)

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

    }
}

答案 3 :(得分:4)

您似乎误解了集合初始化程序的功能。

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

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

如果没有创建集合,显然这会因NullReferenceException而失败。

答案 4 :(得分:0)

在编译时总是要检查引用null。虽然编译器有时会在分配变量之前警告使用变量。编译器正常工作。这是一个运行时错误。

答案 5 :(得分:0)

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

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