我一直在使用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);
这个假设准确吗?
答案 0 :(得分: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 };