可以混合对象初始化器和集合初始化器吗?

我按照此处的说明使用IEnumerable定义了一个集合初始化程序: http : //msdn.microsoft.com/en-us/library/bb384062.aspx

现在我可以在我的集合初始化程序中创建对象,并且我们可以像我这样添加Add()方法:

class ArrangedPanel : RectElement { private List arrangedChildren = new List(); public int Padding = 2; public void Add(RectElement element) { arrangedChildren.Add(element); //do custom stuff here } public IEnumerator GetEnumerator() { return arrangedChildren.GetEnumerator(); } } // Somewhere debugPanel.Add(new ArrangedPanel() { new ButtonToggle(), new ButtonToggle() }); 

但是,如果我尝试设置属性,例如我的“填充”字段,我会在集合初始值设定项上出错。

 debugPanel.Add(new ArrangedPanel() { Padding = 5, new ButtonToggle(), new ButtonToggle() }); 

是否可以设置集合初始化器和对象初始化器?

遗憾的是,无法混合对象和集合初始值设定项。 C#3.0规范将7.5.10.1节中的对象创建表达式定义为:

    对象创建表达式:
       new type (argument-list opt )object-or-collection-initializer opt
        类型的 object-or-collection-initializer

正如您所料, object-or-collection-initializer是对象初始值设定项集合初始值设定项。 没有可用于组合的语法。

我有类似的问题。 最接近的一个可以明显得到的,就是在类中添加一个允许集合初始化程序访问的属性:

ArrangedPanel

 public ArrangedPanel Container { get { return this; } } 

在代码中:

 debugPanel.Add(new ArrangedPanel() { Padding = 5, Container = { new ButtonToggle(), new ButtonToggle() } }); 

还不错,我猜?

@Edit:根据@Tseng的注释,我更改了新属性的返回值,以返回ArrangedObject本身而不是其List成员。 这样就调用了ArrangedPanel.Add方法,并重用了其中任何(可能更复杂的)逻辑。

@ Edit2:重命名属性(’Children’ – >’Container’),希望新名称能更好地反映新的含义。

理论上,存储位置初始化器应该创建具有给定状态的新对象,而不是使对象在一系列状态中前进。 应该通过一系列状态放置某些状态的代码应该写在对象的构造函数中。

使用常量初始化存储位置(字段,变量等)只需将其值设置一次。 使用构造函数或方法调用初始化变量将使该方法在启动之前获得所需的一切,但在该方法返回之前不会对该变量执行任何操作。 因此,它也避免了任何明显的国家inheritance。

属性初始化器和集合初始化器都违反了这种模式,但是假设设计了许多类,以便在暴露对外部代码的引用之前创建对象并设置一些属性,这将产生与瞬间与这些属性即时存在的对象无法区分的结果。 。 同样,集合初始化程序假定创建集合然后使用一系列项目调用Add将产生与保持正确值的存在的集合无法区分的结果。

并非所有暴露属性的类在与属性初始值设定项一起使用时都会产生预期的行为,并且并非所有类看起来像集合初始值设定项时都会产生预期的行为,但编译器愿意“猜测”与之一起使用的类。属性初始值设定项将符合常规属性模式,对于与集合初始值设定项一起使用的类也是如此。

但是,如果设置一个对象需要调用属性设置器和一个item-Add方法,那么这意味着该对象不是具有属性设置器的典型事物,也不是典型的集合。 语言没有特别的理由要求在添加项目之前调用属性设置器,也没有任何特定的理由来指定它们将在之后被调用。 可以允许初始化器的C#源代码指定它们运行的​​顺序,但是这样的规范将明确地确认操作序列将以初始化器本身内未表达的方式影响对象的状态。 这种副作用往往意味着所讨论的代码属于构造函数而不是字段初始化程序。

另一种可能性,没有顺序依赖和类型歧义,虽然非常明确和冗长。

 public class PaddingSetter { public Padding Value { get; private set; } public PaddingSetter() { Value = new Padding(5); } } 

 public void Add(PaddingSetter setter) { Padding = setter.Value; } 

 new ArrangedPanel() { new PaddingSetter(5), new ButtonToggle(), new ButtonToggle() } 

在这种情况下我不建议,但可以使用多个Add重载。

所以在ArrangedPannel包括

 public void Add(int padding) { Padding = padding; } 

然后可以在代码中定义

 debugPanel.Add(new ArrangedPanel() { 5, // is this Padding? new ButtonToggle(), new ButtonToggle() }); 

但我更喜欢@Haymo的答案,因为这里不清楚’5’是什么设置的,多个int属性可能会导致疯狂的代码像

 public void Add(int intProp) { var current = intPropSetCount++; switch(current) { case 0: Padding = intProp; return; case 1: SecondProp = intProp; return; // ... default: throw new Exception(); } } 

这个想法最好留给将多个集合组合成1个包装器。