C# 是否可以混合使用对象初始值设定项和集合初始值设定项?

C# 是否可以混合使用对象初始值设定项和集合初始值设定项?,c#,initialization,ienumerable,C#,Initialization,Ienumerable,我使用IEnumerable定义集合初始值设定项,如下所示: 现在,我可以在集合初始值设定项中创建对象,并使用Add()方法添加它们,如下所示: class ArrangedPanel : RectElement { private List<RectElement> arrangedChildren = new List<RectElement>(); public int Padding = 2; public void Add(RectE

我使用IEnumerable定义集合初始值设定项,如下所示:

现在,我可以在集合初始值设定项中创建对象,并使用Add()方法添加它们,如下所示:

class ArrangedPanel : RectElement
{
    private List<RectElement> arrangedChildren = new List<RectElement>();
    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()
});

是否可以同时设置集合初始值设定项和对象初始值设定项?

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

object-creation-expression: new type ( argument-listopt ) object-or-collection-initializeropt new type object-or-collection-initializer 对象创建表达式: 新类型(参数listopt)对象或集合初始值设定项Opt 新类型对象或集合初始值设定项
正如您所料,
对象或集合初始值设定项
是对象初始值设定项或集合初始值设定项。没有语法可用于将then组合在一起。

我也有类似的问题。显然,最接近的方法是向类添加一个允许集合初始值设定项访问的属性:

排列面板中

public ArrangedPanel Container {
   get { return this; }
}
在代码中:

debugPanel.Add(new ArrangedPanel() 
{ 
    Padding = 5,
    Container = {
        new ButtonToggle(),
        new ButtonToggle()
    }
});
我想还不错吧

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


@Edit2:重命名属性(“Children'->“Container”),希望新名称更好地反映新的含义。

存储位置初始值设定项理论上应该创建具有给定状态的新对象,而不是使对象经历一系列状态。应该让某个对象经历一系列状态的代码应该写在对象的构造函数中

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

属性初始值设定项和集合初始值设定项都违反此模式,但是,操作的前提是,许多类的设计都是这样的:在将引用公开给外部代码之前创建一个对象并设置一些属性,这将产生无法区分的结果,而这些结果与那些属性同时存在的对象是不一样的。同样,集合初始值设定项假定创建一个集合,然后使用一系列项调用
Add
,将产生与持有正确值的集合无法区分的结果

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


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

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

因此在
arrangedPanel
include中

public void Add(int padding)
{
    Padding = padding;
}
然后可以用如下代码定义

debugPanel.Add(new ArrangedPanel() 
{ 
    5, // is this Padding?
    new ButtonToggle(),
    new ButtonToggle()
});
public void Add(int intProp)
{
    var current = intPropSetCount++;
    switch(current)
    {
        case 0: Padding = intProp; return;
        case 1: SecondProp = intProp; return;
        // ...
        default: throw new Exception();
    }
}
但我更喜欢@Haymo的答案,因为这里不清楚“5”设置为什么,多个
int
属性可能会导致像这样的疯狂代码

debugPanel.Add(new ArrangedPanel() 
{ 
    5, // is this Padding?
    new ButtonToggle(),
    new ButtonToggle()
});
public void Add(int intProp)
{
    var current = intPropSetCount++;
    switch(current)
    {
        case 0: Padding = intProp; return;
        case 1: SecondProp = intProp; return;
        // ...
        default: throw new Exception();
    }
}

这种想法最好是将多个集合合并到一个包装器中。

另一种可能,没有顺序依赖和类型歧义,尽管非常明确和冗长

public class PaddingSetter
{
    public Padding Value { get; private set; }

    public PaddingSetter()
    {
        Value = new Padding(5);
    }
}


为什么要将它们混合到一个初始值设定项中?@BoltClock:这提供了一种很好的声明方式来构造对象树,不是吗?@jsmars:当您处理自己创建的类时,您可以添加一个构造函数,该构造函数将要设置的属性作为参数。这样,您仍然可以使用良好的声明方式来构造对象树。不过,如果微软直接添加了对此的支持,那就太酷了。谢谢你的回答,可惜它不起作用。像一个简单的语法更改这样的接缝将使这成为可能,因为我看不出这不起作用的逻辑原因。它们都是“语法糖”,所以肯定没有理由不可能。我猜测为什么没有实现它是因为这是一个非常不寻常的要求,而且语法会有点混乱-在您的示例中,对象初始值设定项位于集合项之前,但为什么不是相反,或者甚至是全部混淆?这样的松散可能意味着语法相当糟糕。请看我的答案,找到一个简单且不太糟糕的解决方法。这是一个不错的解决方法。这是一个相当薄弱的解决方法,因为它破坏了封装,例如
。将调用列表的Add
方法,而不是示例中的
ArrangedPanel
。。。跟丘吉尔说:这是最糟糕的[解决方案],除了所有其他的。@Tseng…等等
new ArrangedPanel() 
{ 
    new PaddingSetter(5),
    new ButtonToggle(),
    new ButtonToggle()
}