C# 对象初始化期间出现NullReferenceException

C# 对象初始化期间出现NullReferenceException,c#,nullreferenceexception,C#,Nullreferenceexception,为什么在下面的代码中尝试设置X的值时会出现NullReferenceException?当我在初始化B时使用new关键字时,它工作得很好,但是为什么它在没有new的情况下编译得很好,然后在运行时失败呢 初始化语法可能很棘手。在代码中,您试图设置a.B.X的值,而不先设置B的值。您的代码转换为: var a = new A(); a.B.X = 1; 。。。这将产生与您现在得到的相同的异常。这是因为a.B被初始化为null,除非您显式地为其创建实例 正如你所指出的,这将起作用: var

为什么在下面的代码中尝试设置X的值时会出现NullReferenceException?当我在初始化
B
时使用
new
关键字时,它工作得很好,但是为什么它在没有
new
的情况下编译得很好,然后在运行时失败呢


初始化语法可能很棘手。在代码中,您试图设置
a.B.X
的值,而不先设置
B
的值。您的代码转换为:

var a = new A();
a.B.X = 1;
。。。这将产生与您现在得到的相同的异常。这是因为
a.B
被初始化为
null
,除非您显式地为其创建实例

正如你所指出的,这将起作用:

    var a=new A{
            B= new _B {
                X=1
            }
        };
您还可以确保
A
的构造函数初始化A
B

    public _B B = new A._B();
为什么它在没有新代码的情况下编译良好,然后在运行时失败

编译器需要做太多的工作才能深入研究
A
类的代码,并意识到
B
此时肯定是空的:正如我指出的,您可以更改
A
构造函数的实现,以确保情况并非如此。这就是空引用异常是最常见的异常类型的原因之一

避免这种情况的最佳策略是将构造函数中的所有字段初始化为非空值。如果在调用构造函数之前您不知道给它们什么值,那么让构造函数将这些值作为参数。如果您希望某个字段不总是有值,那么可以使用可选类型强制程序员在编译时处理该事实

更新2021 现在,C#支持,您可以使用它来鼓励/强制程序员知道您的字段可以为空,如果这是您想要采取的路线的话

    public _B? B;

具体请参见接受答案中的间接部分。因为在您的示例中,您没有初始化B。您基本上执行a.B.X=1,其中B仍然为空。您是在问为什么您得到了NRE,或者为什么编译器没有警告您使用B变量之前没有实例化它?糟糕的编译器!它应该检测到
B={}将返回null
。当您有多个嵌套代码时,很难找到它。我启用了
公共语言运行时异常
,它会这样说
[名称空间].A.B.get返回null。
。它很容易修复
B=newb{}
谢谢,我认为它类似于
int[]a={1,2,3}
。发布问题后,我添加了关键字“嵌套”,并在规范中找到它:在等号后指定对象初始值设定项的成员初始值设定项是嵌套对象初始值设定项,即嵌入式对象的初始化。嵌套对象初始值设定项中的指定将被视为对字段或属性成员的指定,而不是为字段或属性指定新值。嵌套对象初始值设定项不能应用于值类型的属性或值类型的只读字段。“@Franta:我知道你的意思。还要注意,列表初始值设定项语法调用“添加”“而不是创建新列表。因此,在构造函数中总是使用空列表初始化列表属性通常是明智的。我尝试初始化一个int数组:然后它抛出编译错误:“无法使用集合初始值设定项初始化'int[]'类型的对象”(4.5)和“'int[]'不包含'Add'的定义”(Roslyn)。感谢您的解释-我想我会被这些错误消息弄糊涂,不知道发生了什么。@StriplingWarrior您的链接不幸已断开。也许会提供一个新的?@Heki:谢谢你指出这一点。我把图书馆搬到了Github。然而,随着C#添加了可为空的引用类型,我认为该库远不如以前有用。我相应地更新了我的答案。
    public _B? B;