C#嵌套初始化陌生感

Mar*_* S. 4 c# syntax semantics c#-6.0

在这些初始化语句编译的前提下

List<int> l = new List<int> { 1, 2, 3 };
Dictionary<int, int> d = new Dictionary<int, int> { [1] = 11, [2] = 22 };
Foo f = new Foo { Bar = new List<int>() };
Run Code Online (Sandbox Code Playgroud)

这不会

List<int> l = { 1, 2, 3 };
Dictionary<int, int> d = { [1] = 11, [2] = 22 };
Foo f = { Bar = new List<int>() };
Run Code Online (Sandbox Code Playgroud)

我有一个关于嵌套初始化的问题.鉴于以下课程

public class Foo {
    public List<int> Bar { get; set; } = new List<int>();
    public Dictionary<int, Foo> Baz { get; set; } = new Dictionary<int, Foo>();
}
Run Code Online (Sandbox Code Playgroud)

我偶然发现你可以这样做:

Foo f = new Foo {
    Bar = { 1, 2, 3 },
    Baz = {
        [1] = {
            Bar = { 4, 5, 6 }
        }
    }
};
Run Code Online (Sandbox Code Playgroud)

虽然它确实编译它抛出一个KeyNotFoundException.所以我将属性更改为

public List<int> Bar { get; set; } = new List<int> { 4, 5, 6 };
public Dictionary<int, Foo> Baz { get; set; }
    = new Dictionary<int, Foo> { [1] = new Foo { Bar = new List<int>() { 1, 2, 3 } } };
Run Code Online (Sandbox Code Playgroud)

假设这是替换现有成员的一些不寻常的表示法.现在初始化抛出一个StackOverflowException.

所以我的问题是,为什么表达式甚至可以编译?该怎么办?我觉得我必须遗漏一些非常明显的东西.

Jon*_*eet 7

所以我的问题是,为什么表达式甚至可以编译?

它是一个带有集合初始化值的对象初始值设定项.从C#规范部分7.6.10.2:

在等号后面指定集合初始值设定项的成员初始值设定项是嵌入式集合的初始化.而不是将新集合分配给字段或属性,初始化程序中给出的元素将添加到字段或属性引用的集合中.

所以你的代码大致相当于:

Foo tmp = new Foo();
tmp.Bar.Add(1);
tmp.Bar.Add(2);
tmp.Bar.Add(3);
tmp.Baz[1].Bar.Add(4); // This will throw KeyNotFoundException if Baz is empty
tmp.Baz[1].Bar.Add(5);
tmp.Baz[1].Bar.Add(6);
Foo f = tmp;
Run Code Online (Sandbox Code Playgroud)

你的初始化版本会抛出一个StackOverflowException因为初始化器Foo需要创建一个新的实例Foo,它需要创建一个新的实例Foo等.