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" }
}
};
这不是语法错误,是你在一个没有实例化的属性上使用了对象初始化器。您所写的内容可以扩展到
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