【问题标题】:Initializing list property without "new List" causes NullReferenceException在没有“新列表”的情况下初始化列表属性会导致 NullReferenceException
【发布时间】:2015-11-27 07:32:47
【问题描述】:
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" }
         }
      };
   }
}

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

如果您在上面的 sn-p 中注意到,我在初始化子属性时省略了 new。

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

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

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

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

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

【问题讨论】:

    标签: c# nullreferenceexception


    【解决方案1】:

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

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

    抛出NullReferenceException:您正在尝试分配属性Child 包含的属性Strings,而Child 仍然是null。 首先使用构造函数来实例化Child,处理这个问题。

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • 集合初始化器与只读属性无关。此代码有效是因为您将 Strings = new List&lt;string&gt;(); 放入构造函数中,而不是因为属性具有 private set。
      • 再次阅读代码。 Child 属性不是集合,它是只读属性。
      • 科林的回答具有欺骗性,因此是错误的,@Ajai。它指出只读属性和集合初始化器的行为之间存在联系。这不是真的。集合初始值设定项和只读属性不必相互做任何事情,它们是完全不相关的概念。请参阅this answer 我已链接到。
      • 矩形初始值设定项是object-initializer。 OP 所说的初始化程序是collection-initializer。 They are different。集合初始值设定项由编译器转换为系列或对Add() 方法的调用,而不是其他任何东西。此行为与只读属性或object-initializers 无关。
      • 您的下一句,如果您更改代码以在各自的构造函数中初始化 Child 和 Strings 属性,则语法有效 是正确的,如果这样,我会支持您的答案声明后面是“因为对象初始化器不创建对象,它们只设置属性或将项目添加到集合”而不是“它用于只读属性”。
      【解决方案3】:

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

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

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

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

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

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

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

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

          【讨论】:

            【解决方案6】:

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

            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
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2019-04-12
              • 2011-08-09
              • 1970-01-01
              • 2013-08-07
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多