集合初始化表达式的这两种变体有什么区别?

What is the difference between these two variations of collection initialiser expressions?

我使用 C# 有一段时间了,但最近注意到我的一个单元测试的行为根据我使用的集合初始化表达式的变体而改变:

到目前为止,我假设第二种形式只是语法糖,在语义上等同于第一种形式。但是,在这两种形式之间切换导致我的单元测试失败。

下面的示例代码演示了这一点:

void Main()
{
    var foo1 = new Foo { Items = new List<int> { 1, 2, 3} };
    var foo2 = new Foo { Items = { 1, 2, 3 } };

    foo1.Dump();
    foo2.Dump();
}

class Foo
{
    public List<int> Items { get; set; }
}

当我 运行 这样做时,第一个赋值工作正常,但第二个结果是 NullReferenceException

我的直觉是,编译器在后台将这两个表达式视为:

var foo1 = new Foo();
foo1.Items = new List<int> { 1, 2, 3 }; 

var foo2 = new Foo();
foo2.Items.Add(1);
foo2.Items.Add(2);
foo2.Items.Add(3);

这个假设准确吗?

是的,您的假设是准确的。如果一个对象初始值设定项只有:

{
    Property = { ... }
}

而不是

{
    Property = expression
}

然后 setter 不使用 属性 - getter使用,然后调用 Add 方法,或者在返回值中设置属性。所以:

var foo = new Foo
{
    Collection = { 1 }
    Property =
    {
        Value = 1
    }
};

相当于:

// Only the *getters* for Collection and Property are called
var foo = new Foo();
foo.Collection.Add(1);
foo.Property.Value = 1;

比较:

var foo = new Foo
{
    Collection = new List<int> { 1 },
    Property = new Bar { Value = 1 }
};

相当于:

// The setters for Collection and Property are called
var foo = new Foo();
foo.Collection = new List<int> { 1 };
foo.Property = new Bar { Value = 1 };