20

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

  • var object = new Class { SomeCollection = new List<int> { 1, 2, 3 } };
  • var object = new Class { SomeCollection = { 1, 2, 3 } };

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

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

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);

这个假设准确吗?

4

1 回答 1

22

是的,你的假设是正确的。如果一个对象初始化器只有:

{
    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 };
于 2017-07-31T07:38:26.413 回答