Что геттеры и сеттеры должны и не должны делать [дубликаты]

Возможный дубликат:
Конвенционный вопрос: когда вы используете функцию Getter/Setter, а не свойство?

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

Мой предыдущий вопрос получил немедленный комментарий ( позже удалено), что указанные сеттеры не должны иметь побочных эффектов, и метод SetProperty был бы лучшим выбором.

Действительно, похоже, что это мнение Microsoft также. Однако их свойства часто вызывают события, такие как Resized, когда установлено свойство формы Width или Height. OwenP также заявляет, что «вы не должны позволять свойству вызывать исключения, свойства не должны иметь побочных эффектов, порядок не должен иметь значения, и свойства должны возвращаться относительно быстро».

Тем не менее Майкл Стам утверждает, что при проверка данных в сеттере. Если ваш установщик не выдает исключение, как вы можете эффективно проверять данные, так как многие ответы на этот вопрос предложить?

Как насчет того, когда вам нужно вызвать событие, как это делают почти все элементы управления Microsoft? Разве вы не во власти тех, кто подписался на ваше мероприятие? Если их обработчик выдает огромное количество информации или сам выдает ошибку, что происходит с вашим сеттером?

Наконец, как насчет ленивой загрузки внутри геттера? Это также может нарушить предыдущие рекомендации.

Что допустимо поместить в геттер или сеттер, а что следует оставить только в методах доступа?

Изменить:

Из другой статьи в MSDN :

Методы get и set в целом ничем не отличаются от других методов. Они могут выполнять любую программную логику, генерировать исключения, переопределяться и объявляться с любыми модификаторами, разрешенными языком программирования. Обратите внимание, однако, что свойства также могут быть статическими. Если свойство является статическим, существуют ограничения на то, что могут делать методы get и set. Подробности смотрите в справочнике по языку программирования.


person dlras2    schedule 25.05.2010    source источник
comment
Я прочитал этот вопрос, ответы и даже связался с ним в своем собственном вопросе. Я ничего не видел о возбуждении событий, которые, как мне кажется, заслуживают разных ответов.   -  person dlras2    schedule 26.05.2010
comment
@Rowland: я думаю, что это достаточно конкретно, чтобы отличаться.   -  person Steven Sudit    schedule 26.05.2010
comment
Кто проголосовал за то, чтобы это было перенесено в Super User?   -  person ChrisF    schedule 26.05.2010
comment
Он относится к ряду других вопросов, но не является ТОЧНОЙ копией, поскольку охватывает новую область.   -  person Steven Sudit    schedule 26.05.2010
comment
Количество других вопросов, которые я нашел, я также связал, чтобы люди могли видеть информацию, которую я не нашел решающей, вместо того, чтобы просто связать меня с вопросами, которые я уже прочитал, или закрыть это как дубликат. Видимо, я просто облегчил им работу.   -  person dlras2    schedule 26.05.2010


Ответы (3)


Мой вид:

  1. Если ожидается, что сеттер или геттер будет дорогим, не делайте его свойством, сделайте его методом.

  2. Если установка свойства вызывает события из-за изменений, это нормально. Как еще вы позволите слушателям получать уведомления об изменениях? Однако вы можете предложить пару BeginInit/EndInit для подавления событий до тех пор, пока не будут внесены все изменения. Обычно обработчик события отвечает за своевременный возврат, но если вы действительно не можете доверять ему, то вы можете захотеть сигнализировать о событии в другом потоке.

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

  4. Доступ к свойству может иметь побочные эффекты, если они не являются неожиданными и не имеют значения. Это означает, что создание экземпляра JIT в геттере в порядке. Точно так же установка грязного флага для экземпляра всякий раз, когда вносятся изменения, вполне допустима, так как при этом устанавливается связанное свойство, например, другой формат для одного и того же значения.

  5. Если он что-то делает, а не просто обращается к значению, это должен быть метод. Method — это глаголы, поэтому создание соединения будет выполняться методом OpenConnection(), а не свойством Connection. Свойство Connection будет использоваться для получения используемого соединения или для привязки экземпляра к другому соединению.

изменить – добавить 5, изменить 2 и 3

person Steven Sudit    schedule 25.05.2010

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

Что касается генерации исключений, если вы устанавливаете для свойства недопустимое значение (в самом простом смысле), то исключения проверки допустимы. Однако, если установщик выполняет целую серию сложных проверок бизнес-правил, или пытается обновить другие объекты, или делает что-то еще, что может вызвать исключение, то это плохо. Но эта проблема на самом деле связана не с самим исключением, а скорее с тем, что сеттер отключается и тайно выполняет множество функций, которые вызывающая сторона не ожидает (или не должна) ожидать.

То же и с событиями. Если установщик генерирует событие, говорящее, что «это свойство изменилось», то все в порядке, потому что это очевидный побочный эффект. Но если он запускает какое-то другое пользовательское событие, вызывая выполнение какого-то скрытого фрагмента кода в другой части системы, это плохо.

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

person Mike Mooney    schedule 25.05.2010
comment
Избегание лени может привести к значительным потерям производительности, поэтому я не думаю, что это хорошая идея в целом. - person Steven Sudit; 26.05.2010
comment
@Steven Sundit: Да, это компромисс, и это зависит от ситуации. Если предварительная загрузка приводит к существенным затратам на производительность, используйте отложенную загрузку или загрузку, зависящую от конкретной ситуации. Но часто (и по моему опыту, обычно) это не так. - person Mike Mooney; 26.05.2010
comment
Предварительная загрузка, безусловно, может быть подходящим выбором, особенно когда она дешевая или одноразовая. Когда это не так, JIT становится вынужденным шагом. - person Steven Sudit; 26.05.2010

Я всегда считал консервативный подход лучшим, когда работаю на C#. Поскольку свойства синтаксически такие же, как и поля, они должны работать как поля: никаких исключений, никакой проверки, никаких шуток. (Действительно, большинство моих свойств начинаются как простые поля и не становятся свойствами до тех пор, пока они не станут абсолютно необходимыми.) Идея состоит в том, что если вы видите что-то, похожее на получение или установку набора полей, то это ЯВЛЯЕТСЯ чем-то вроде получения или установка поля с точки зрения функциональности (никаких исключений), общей эффективности (например, установка переменных не запускает каскад вызовов делегатов) и влияния на состояние программы (установка переменной устанавливает эту переменную, а не не вызывает множество делегатов, которые могут делать что угодно).

Разумные вещи для набора свойств включают установку флага, указывающего, что произошло изменение:

set {
    if(this.value!=value) {
        this.changed=true;
        this.value=value;
    }
}

Возможно, на самом деле установите значение для другого объекта, например:

set { this.otherObject.value=value; }

Возможно, немного распутать ввод, чтобы упростить внутренний код класса:

set {
    this.isValid=(value&Flags.IsValid)!=0;
    this.setting=value&Flags.SettingMask;
}

(Конечно, в этих двух последних случаях функция get вполне может делать обратное.)

Если должно произойти что-то более сложное, в частности, вызов делегатов, выполнение проверки или создание исключений, то я считаю, что функция лучше. (Довольно часто мои поля превращаются в свойства с помощью get и set, а затем становятся свойством get и функцией set.) Аналогично для геттеров; если вы возвращаете ссылку на что-то, это не проблема, но если вы создаете совершенно новый большой объект и заполняете его каждый раз при чтении свойства — не так уж и круто.

person Community    schedule 25.05.2010
comment
Если вы работаете на C#, то должны знать о WPF, который полностью зависит от изменений свойств, побочным эффектом которых является сигнализация этих изменений. Я не понимаю, как мы можем последовать вашему совету, не нанеся вред нашему коду. - person Steven Sudit; 26.05.2010
comment
Когда вы говорите о побочном эффекте сигнализации об этих изменениях, вы имеете в виду создание события? Или вы хотите сказать, что if(this.value!=value) return; было бы плохой идеей? Я не работал с WPF. (То, что я программирую на С#.) - person dlras2; 26.05.2010
comment
@Cyclotis: Ну, это относится не только к WPF, но WPF особенно очевидно использует уведомление об изменении свойств. Вы также найдете примеры во многих местах, где используется привязка данных. Если нам не разрешено запускать события при изменении, что у нас есть вместо этого? Опрос? - person Steven Sudit; 26.05.2010
comment
Я все еще немного запутался - С# не создает автоматически уведомления об изменении свойств, не так ли? Вам все равно придется создавать события вручную, верно? В большинстве моих сеттеров есть следующее: if (State == value) return; _state = value; if (StateChanged != null) OnStateChanged(EventArgs.Empty); - person dlras2; 26.05.2010
comment
@Cyclotis: Нет, это не автоматически, но обязательно для определенных видов привязки. См. msdn.microsoft.com/en-us/library/ms743695.aspx - person Steven Sudit; 26.05.2010
comment
Я не использую WPF, только WinForms. (Как ни странно, я также большой поклонник опросов, а не событий...) Если WPF заставляет вас что-то делать, вы застряли, и вам просто придется это сделать, независимо от того, что я думаю :) - person ; 26.05.2010
comment
Это не просто WPF, но WPF — идеальный случай. Взгляните на привязку данных в ASP.NET или WinForms, и вы найдете различные события уведомлений, которые запускаются изменениями свойств. Что касается опроса, то это не масштабируемое решение, особенно если речь идет о многоядерных системах. - person Steven Sudit; 26.05.2010