Подтвердить что ты не робот

Можно ли смешивать инициализатор объектов и инициализатор коллекции?

Я определяю инициализатор коллекции с 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()
});

Однако, если я попытаюсь установить свойство, например, мое поле "Заполнение", я получаю сообщение об ошибке в инициализаторах коллекции.

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

Можно ли установить инициализаторы коллекции и инициализаторы объектов?

4b9b3361

Ответ 1

К сожалению, невозможно смешивать инициализаторы объектов и коллекций. Спецификация С# 3.0 определяет выражение создания объекта в разделе 7.5.10.1 как:

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

Как и следовало ожидать, object-or-collection-initializer является либо инициализатором объекта, либо инициализатором коллекции. Отсутствует синтаксис для объединения вместе.

Ответ 2

У меня была аналогичная проблема. Ближайшим, по-видимому, может быть, является добавление свойства к классу, который позволяет доступ к инициализатору коллекции:

В ArrangedPanel:

public ArrangedPanel Container {
   get { return this; }
}

И в коде:

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

не так уж плохо, наверное?

@Edit: в соответствии с комментарием @Tseng я изменил возвращаемое значение нового свойства, чтобы вернуть сам ArrangedObject, а не его член List<RectElement>. Таким образом вызывается метод ArrangedPanel.Add, и любая (потенциально более сложная) логика в нем используется повторно.

@Edit2: переименовано свойство ( "Дети" → "Контейнер" ) в надежде, что новое имя лучше отражает новое значение.

Ответ 3

Инициализаторы хранения-хранилища теоретически предполагают создание новых объектов с заданным состоянием, в отличие от того, чтобы объекты проходили через ряд состояний. Код, который должен помещать что-то через ряд состояний, должен быть записан в конструкторе объекта.

Инициализация места хранения (поле, переменная и т.д.) с константой просто устанавливает его значение один раз. Инициализация переменной вызовом конструктора или метода приведет к тому, что методу будет предоставлено все, что потребуется, прежде чем он запустится, но ничего не изменит для переменной до тех пор, пока метод не вернется. Таким образом, он также избегает любой очевидной последовательности состояний.

Инициализаторы инициализаторов и инициализаторы коллекций нарушают этот шаблон, но действуют в предположении, что многие классы сконструированы таким образом, что создание объекта и установка некоторых свойств перед обнажением ссылки на внешний код приведут к результатам, неотличимым от мгновенного появления объекта с этими свойствами. Аналогично, инициализаторы коллекции предполагают, что создание коллекции, а затем вызов Add с последовательностью элементов приведет к результатам, неотличимым от существующей коллекции, содержащей правильные значения.

Не все классы, которые выставляют свойства, вызывают ожидаемое поведение при использовании с инициализаторами свойств, и не все классы выглядят как коллекции, что приведет к ожидаемому поведению при использовании с инициализаторами коллекции, но компилятор готов "угадать" те классы, которые используются с инициализаторами свойств, будут соответствовать шаблону нормального свойства, а также для классов, которые используются с инициализаторами коллекции.

Если для настройки объекта потребуются как средства определения свойств вызова, так и метод item-Add, это будет означать, что объект не является типичным с установщиками свойств и не является типичной коллекцией. Не было бы никакой особой причины, по которой язык определял бы, что определители свойств будут вызваны до добавления элементов, а также не будет какой-либо конкретной причины, чтобы указать, что они будут вызваны после. Можно позволить исходному коду С# для инициализаторов указать порядок, в котором они выполняются, но такая спецификация явно признает, что последовательность операций повлияет на состояние объекта способами, не выраженными в самом инициализаторе. Такие побочные эффекты будут иметь тенденцию подразумевать, что рассматриваемый код относится к конструктору, а не к инициализатору поля.

Ответ 4

Другая возможность, свободная от порядка зависимость и неоднозначность типа, хотя и очень явная и длительная.

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

Ответ 5

В этом случае я не рекомендую, но можно использовать несколько перегрузок 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 оболочку.