可以混合对象初始值设定项和集合初始值设定项吗?

发布于 2024-12-07 23:52:50 字数 963 浏览 3 评论 0原文

我按照此处的指示使用 IEnumerable 定义集合初始值设定项: http://msdn.microsoft.com/en-us/library/bb384062.aspx

现在我可以在集合初始值设定项中创建对象,并使用我的 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()
});

但是,如果我尝试设置属性,例如我的“Padding”字段,我在集合初始值设定项上收到错误。

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

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

I define an collection initializer with IEnumerable as instructed here:
http://msdn.microsoft.com/en-us/library/bb384062.aspx

Now I'm able to create objects within my collection initializer and they are added wih my Add() method like so:

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

However, if I try to set a property, such as my "Padding" field, I get an error on the collection initializers.

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

Is it possible to set both collection initializers and object initializers?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

巷雨优美回忆 2024-12-14 23:52:50

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

在 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'),希望新名称更好地体现新含义。

I had a similar problem. The closest one can obviously get, is to add a property to the class which allows the collection initializer access:

In ArrangedPanel:

public ArrangedPanel Container {
   get { return this; }
}

And in code:

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

not too bad, I guess ?

@Edit: according to the comment by @Tseng I changed the return value of the new property to return the ArrangedObject itself instead of its List<RectElement> member. This way the ArrangedPanel.Add method is called and any (potentially more complex) logic in it is reused.

@Edit2: renamed the property ('Children' -> 'Container') in the hope that the new name better reflects the new meaning.

捂风挽笑 2024-12-14 23:52:50

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

    object-creation-expression:
      new   type   (   argument-listopt   )   object-or-collection-initializeropt
      new   type   object-or-collection-initializer

正如您所期望的,object-or-collection-initializer 是对象初始值设定项集合初始化程序。没有可用于将 then 组合在一起的语法。

Unfortunately it is not possible to mix object and collection initializers. The C# 3.0 specification defines an object creation expression in section 7.5.10.1 as:

    object-creation-expression:
      new   type   (   argument-listopt   )   object-or-collection-initializeropt
      new   type   object-or-collection-initializer

As you might expect, object-or-collection-initializer is either an object initializer or a collection initializer. There is no syntax available for combining then together.

时光沙漏 2024-12-14 23:52:50

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

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()
}

Another possibility, free of order dependency and type ambiguities, though very explicit and lengthy.

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()
}
小姐丶请自重 2024-12-14 23:52:50

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

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

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

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

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

Storage-location initializers are in theory supposed to create new objects with a given state, as opposed to causing objects to progress through a series of states. Code which is supposed to put something through a series of states is supposed to be written in an object's constructor.

Initializing a storage location (field, variable, etc.) with a constant simply sets its value once. Initializing a variable with a constructor or method call will cause the method to be given everything it will need before it starts, but will do nothing to the variable until the method returns. Thus it too avoids any apparent succession of states.

Property initializers and collection initializers both violate this pattern, but operate on the assumption that many classes are designed so that creating an object and setting some properties before exposing the reference to outside code will yield results indistinguishable from an object instantaneously coming into existence with those properties. Likewise, collection initializers assume that creating a collection and then calling Add with a sequence of items will yield results indistinguishable from the collection coming into existence holding the correct values.

Not all classes which expose properties will yield the expected behavior when used with property initializers, and not all classes look like collections will yield the expected behavior when used with collection initializers, but the compiler is willing to "guess" that classes which are used with property initializers will conform to the normal property pattern, and likewise for classes which are used with collection initializers.

If setting up an object would require both calling property setters and an item-Add method, however, that would imply that the object is not a typical things with property setters, nor is it a typical collection. There would be no particular reason for the language to dictate that property setters would be invoked before items are added, nor would there any particular reason to specify that they would be invoked after. One could allow the C# source code for the initializers to specify the order in which they run, but such specification would explicitly acknowledge that the sequence of operations would affect the object's state in ways not expressed within the initializer itself. Such side-effects would tend to imply that the code in question belongs within a constructor rather than a field initializer.

汐鸠 2024-12-14 23:52:50

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

因此,在 ArrangedPannel 中,包含

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

then 可以在代码中定义,

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个包装器。

I don't recommend in this case, but it's possible to use multiple Add overloads.

So in ArrangedPannel include

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

Then can defined in code like

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

But I prefer @Haymo's answer because here it's not clear what '5' is set to, and multiple int properties will probably lead to crazy code like

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

This idea is probably best left for combining multiple collections into 1 wrapper.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文