Должен ли я использовать один раз установить переменные?

Это пахнет?
У меня есть несколько свойств, которые вы можете установить только один раз. Они могут быть установлены в любое время в течение существования объектов и не могут быть отменены.
Я реализую тогда так.

    private FooThingy _foo;
    public FooThingy Foo
    {
        set { if (null == _foo) _foo = value; }
        get { return _foo; }
    }

Но мне это не нравится. Я чувствую, что что-то упустил. Есть я?

Изменить: Почему у меня нет этого в конструкторе.
Я не могу вдаваться в подробности, но установка этого приводит к <плохая аналогия> Таяние восковой статуи </ плохая аналогия>, И я не хочу, чтобы мой конструктор создавал уже расплавленные объекты.

8 ответов

Решение

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

Инициализируйте их в конструкторе.
В качестве альтернативы, если они слишком сложны, чтобы написать все конструкторы, используйте класс factory / builder:

ThingieBuilder tb = new ThingieBuilder();
tb.FooThingy = 17.23;   // r/w properties
tb.BarThingy = 42;
tb.UseExtendedThingamagicAdapter = true;
Thingie t = tb.Create();
if (t.Bar==42) // r/o property
  ...

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

Я думаю, что дизайн будет более понятным для вызывающей стороны, если такие значения будут переданы в конструктор и вместо этого будут отображаться как свойства только для чтения. Если значение не может быть установлено во время построения, возможно, было бы целесообразно выдать исключение при попытке установить значение снова:

private FooThingy _foo;
public FooThingy Foo
{
    set 
    { 
        if (null == _foo) { _foo = value; }
        else  { throw new WhatEverThatFitsException(); }
    }
    get { return _foo; }
}

Просто чтобы быть очень ясным: я никоим образом не поощряю использование свойств set-Once; Пример кода показывает только подход, который я мог бы использовать, если значение по какой-либо причине недоступно во время создания объекта. Это сказал; Я никогда не сталкивался с такой ситуацией ни в одном из проектов, в которых участвовал.

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

Если вы собираетесь сделать это таким образом, бросьте какое-то исключение. По крайней мере, дайте разработчику понять, что вы не установили их значения, а не игнорировали их молча.

Просто создайте методы-установщики, а не свойства - и разрыв между ожиданием и поведением исчезнет. Метод может свободно генерировать исключение InvalidOperationException, но неожиданно сделать так, чтобы установщик свойства сделал это.

private FooThingy _foo;
public FooThingy Foo
{
    get { return _foo; }
}

public void SetFooThingy(FooThingy value) {
   if (Foo != null) 
      throw new InvalidOperationException("Foo is already set!");
   _foo = value;
}

Я согласен с Фредриком, только чтение будет иметь больше смысла. Таким образом, вы можете объявить свою переменную только в конструкторе (или как часть объявления). Я думаю, что делать то, что вы делаете, не сразу ясно, что это то, чего вы хотите достичь.

Проверьте эту страницу MSDN.

Если FooThingy является объектом-значением, таким как int, он будет инициализирован 0 вместо нуля. В остальном все выглядит хорошо

Сделайте их только для чтения и установите их в конструкторе.

Сложно сказать по этому игрушечному примеру, но звучит так _foo относительно важно для существования этого объекта. В этом случае это почти наверняка должно быть свойство только для чтения, которое инициализируется во время создания и не должно иметь никакого открытого установщика. Пользуйтесь неизменными объектами везде, где это возможно; это делает вашу жизнь намного проще!

Другие вопросы по тегам