在没有 "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
包含的 属性 Strings
而 Child
还是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
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
包含的 属性 Strings
而 Child
还是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