为什么对象和集合初始值设定项的组合使用Add方法?

Sta*_*sky 6 c# collections initialization

以下对象和集合初始值设定项的组合不会产生编译错误,但它根本就是错误的(https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object- and-collection-initializers #examples),因为该Add方法将用于初始化:

public class Foo
{
    public List<string> Bar { get; set; }
}

static void Main()
{
    var foo = new Foo
    {
        Bar =
        {
            "one",
            "two"
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

所以你会得到NullReferenceException.在开发语言语法时做出这种不安全决定的原因是什么?例如,为什么不使用新集合的初始化?

Iva*_*oev 4

首先,它不仅仅适用于对象和集合初始值设定项的组合。您在这里所指的称为嵌套集合初始值设定项,相同的规则(或您认为的问题)适用于嵌套对象初始值设定项。因此,如果您有以下课程:

\n\n
public class Foo\n{\n    public Bar Bar { get; set; }\n}\n\npublic class Bar\n{\n    public string Baz { get; set; }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

并且您使用以下代码

\n\n
var foo = new Foo\n{\n    Bar = { Baz = "one" }\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

您将在运行时获得相同的 NRE,因为不会new Bar创建任何 NRE,但会尝试Baz设置Foo.Bar.

\n\n

一般来说,对象/集合初始值设定项的语法是

\n\n
target = source\n
Run Code Online (Sandbox Code Playgroud)\n\n

其中source可以是表达式、对象初始值设定项或集合初始值设定项。请注意,new List<Bar> { \xe2\x80\xa6 }不是集合初始值设定项 - 它是与集合初始值设定项相结合的对象创建表达式(毕竟,一切都是对象,包括集合)。区别在于 - 这个想法不是省略,而是让您选择使用创建表达式 + 对象/集合初始值设定项或使用初始值设定项。new

\n\n

不幸的是,C# 文档没有解释这个概念,但 C# 规范在“对象初始值设定项”部分中解释了这一点:

\n\n
\n

在等号之后指定对象初始值设定项的成员初始值设定项是嵌套对象初始值设定项,即嵌入对象的初始化。嵌套对象初始值设定项中的赋值将被视为对字段或属性的成员的赋值,而不是向字段或属性赋值新值。嵌套对象初始值设定项不能应用于具有值类型的属性,也不能应用于具有值类型的只读字段。

\n
\n\n

\n\n
\n

在等号后指定集合初始值设定项的成员初始值设定项是嵌入式集合的初始化。初始化器中给定的元素将添加到目标引用的集合中,而不是将新集合分配给目标字段、属性或索引器。

\n
\n\n
\n\n

那么这是为什么呢?首先,因为它显然完全按照您的指示执行。如果您需要new,则使用new,否则它可以用作分配(或为集合添加)。

\n\n

其他原因是 - 目标属性无法设置(其他答案中已提到)。但它也可能是不可创建的类型(例如接口、抽象类),即使它是一个具体的类,除了它是一个结构体之外,它将如何决定它应该使用(new List<Bar>new Bar在我的示例中)而不是new MyBarList,如果我们有

\n\n
class MyBarList : List<Bar> { }\n
Run Code Online (Sandbox Code Playgroud)\n\n

或者new MyBar如果我们有

\n\n
class MyBar : Bar { }\n
Run Code Online (Sandbox Code Playgroud)\n\n

正如您所看到的,编译器无法做出这样的假设,因此在我看来,该语言功能被设计为以非常清晰和合乎逻辑的方式工作。唯一令人困惑的部分可能是运算符用于=其他用途,但我想这是一个权衡决定 - 使用相同的运算符=new在需要时添加。

\n