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

Что Getters и сеттеры должны и не должны делать

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

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

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

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

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

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

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

Что приемлемо для размещения в getter или setter, и что следует хранить только в методах доступа?

Edit:

От другого статьи в MSDN:

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

4b9b3361

Ответ 1

Мой взгляд:

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

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

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

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

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

edit - добавлено 5, изменено 2 и 3

Ответ 2

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

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

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

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

Ответ 3

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

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

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.) Аналогично для getters; если вы возвращаете ссылку на что-то, это не проблема, но если вы создаете совершенно новый большой объект и заполняете его каждый раз, когда свойство читается - не так жарко.