初始化器语法

31

我喜欢C# 3的初始化语法并经常使用它,但是今天在Reflector中查看时,出现了以下内容:

var binding = new WSHttpBinding
{
  ReaderQuotas = { MaxArrayLength = 100000 },
  MaxReceivedMessageSize = 10485760
};

一开始我还以为是个错误,但它确实能编译!看来我仍然在不断学习新东西。:)

从我的了解中,它设置了WSHttpBindingReaderQuotas属性的MaxArrayLength属性。

这个语法是创建一个新的ReaderQuotas对象并设置属性,还是假设属性已经初始化?这是初始化“子”属性的通用方式吗?

我觉得这个语法有点令人困惑...


您可能会对这篇文章感兴趣,它讨论了这种类型的初始化器的语义和用例。 - ChaseMedallion
2个回答

23

不,除非你使用= new SomeType {...},否则这不会创建新对象。

var binding = new WSHttpBinding
{
    ReaderQuotas = new XmlDictionaryReaderQuotas { MaxArrayLength = 100000 },
    MaxReceivedMessageSize = 10485760
};

您的示例展示了设置 已存在 的子对象属性的初始化程序语法。还有一种类似的语法可以用于在集合上调用“Add”方法。

您的代码在大体上与以下示例相似:

var binding = new WSHttpBinding();
binding.ReaderQuotas.MaxArrayLength = 100000;
binding.MaxReceivedMessageSize = 10485760;

看起来和那个一模一样,除了语法上的差异还有别的吗?看起来(除非在WSHttpBinding的构造函数中初始化ReaderQuotas),它应该会抛出一个异常。 - James
谢谢,这让我有点惊讶 :) 我猜你所说的“Add”是指像集合初始化器一样的东西? - leppie
@james:在那种情况下,我会期望出现异常,但如果你不知道构造函数中执行了什么操作,则没有源代码可能会令人困惑。一些构造函数重载可以或不可以初始化该属性,这将导致更多的混淆。 - leppie
2
这个与 C# 有关的内容在《C# 深度剖析》第八章中有讨论。该章节的第一版可从 http://manning.com/skeet/ 免费获取。 - Jon Skeet
2
詹姆斯,这个差别微妙。虽然代码在语义上等同于那个,但我们实际上生成了“temp = new Binding(); blah blah blah; var binding = temp;”。为什么?因为变量“binding”不能在其自身的初始化器内使用!那将是一个鸡生蛋问题。如果我们按照马克的建议完全生成代码,那么“binding”将在初始化器代码运行之前初始化,并且某些人可能会观察到“binding”处于部分初始化状态。 - Eric Lippert
我可能错了,但如果赋值中有多个属性,它也会略有不同,因为在初始化器语法中成员只被评估一次 - 因此 ..., SomeProp = { Foo = 1, Bar = 2 } 将是 var tmp = obj.SomeProp; tmp.Foo = 1; tmp.Bar = 2;,而不是 obj.SomeProp.Foo = 1; obj.SomeProp.Bar = 2; - Marc Gravell

15

我同意,这可能有些令人困惑。

你应该阅读规范的7.6.10.2章节,那里有详细的解释。例如:

在等号后指定表达式的成员初始化器将与对字段或属性的赋值相同处理。

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

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接