Якщо властивості мають побічні ефекти


19

Чи повинні властивості в C # мати побічні ефекти, окрім повідомлення про зміну стану?

Я бачив властивості, які використовуються різними способами. З властивостей, які завантажують цінність уперше, до них звертаються до властивостей, які мають величезні побічні ефекти, такі як спричинення переспрямування на іншу сторінку.



1
Серед висновків @ Job, чому я повинен уникати використання властивостей у C #? aka думка Джеффрі Ріхтера дуже важлива для цього питання.
rwong

@rwong Відповідне так, але абсолютно не має сенсу - все-таки його слід читати (щоб бути в курсі проблем, які він висвітлює). Принаймні з цього приводу я перебуваю в таборі Скіта.
Апоорв Хурасія

Хоча це стосується конструкторів, це також актуально: programmers.stackexchange.com/a/164255/1996
Джим Г.

Відповіді:


40

Я припускаю , що ви говорите тільки для читання властивостей, або принаймні власності видобувачів , так як властивість сетера є майже в кожному випадку, будуть мати побічні ефекти. Інакше це не дуже корисно.

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

Взагалі , це означає, що власники нерухомості не повинні мати побічних ефектів.

Однак давайте уважно ставимося до того, що ми маємо на увазі під «побічним ефектом».

Побічний ефект є, технічно, будь-яким зміною стану. Це може бути загальнодоступний стан, або ... це може бути повністю приватна держава.

Ледачі / відкладені навантажувачі - один із прикладів стану, який майже виключно є приватним. Поки ви не зобов'язані абонента звільнити цей ресурс, ви фактично зменшите сюрприз та складність загалом, використовуючи відкладену ініціалізацію. Зазвичай абонент не сподівається явно сигналізувати про ініціалізацію внутрішньої структури. Отже, лінива ініціалізація не порушує вищезазначеного принципу.

Ще один приклад - синхронізована властивість. Для того, щоб метод був безпечним для потоків, його часто доведеться захищати критичним розрізом або мютекс. Введення критичного розділу або придбання мютексу є побічним ефектом; ви змінюєте стан, як правило, глобальний стан. Однак цей побічний ефект необхідний для того, щоб запобігти набагато гіршому вигляду сюрпризу - здивування даних, що змінюються (або ще гірше, частково модифікуються) іншим потоком.

Тому я трохи послаблюю обмеження на наступне: Зчитування властивості не повинно мати видимих побічних ефектів або побічних ефектів, які змінюють їх семантику .

Якщо немає можливого способу, щоб абонент коли-небудь зазнав впливу або навіть знав про побічний ефект, то цей побічний ефект не приносить ніякої шкоди. Якщо вам неможливо було б написати тест, щоб перевірити існування певного побічного ефекту, то його достатньо локалізувати, щоб позначити його як приватну детальну інформацію про реалізацію, і, таким чином, не мати законних проблем для зовнішнього світу.

Але будьте обережні; як правило, слід намагатися уникати побічних ефектів, тому що часто те, що ви можете вважати приватною деталлю впровадження, може несподівано просочитися та стати загальнодоступним.


2
+1 - приємна повна відповідь, включаючи складніші випадки, які перетворюють "повинен" на "повинен ... крім випадків, коли ...."
quick_now

Ще один приклад прийнятного побічного ефекту на геттер: Функція реєстрації журналів.
Лорен Печтел

5

Я відповім на ваше запитання запитанням: Що відбувається, коли ви змінюєте властивість форми Width?

Просто в верхній частині моєї голови, це буде:

  • Змініть значення поля резервного копіювання.
  • Пожежа подія.
  • Змініть розміри форми, повідомте про зміну менеджеру вікон та вимагайте перефарбування.
  • Повідомте елементи управління на формі зміни, щоб будь-який із них, яким потрібно змінити розмір або розташування при зміні форми, зміг зробити це.
  • Викличте всі елементи управління, які змінилися на події пожежі, і скажіть менеджеру вікон, що їх потрібно переробити.
  • Напевно, і інші речі.

(Відмова: Я розробник Delphi, а не розробник .NET. Це може бути не на 100% точним для C #, але я думаю, що він досить близький, особливо враховуючи, наскільки основна версія .NET основана на Delphi.)

Усі ці "побічні ефекти" трапляються, коли ви змінюєте властивість Width у формі. Чи здається вам якийсь із них недоречним чи неправильним?


до тих пір, поки воно не перейде в нескінченну петлю, що викликає себе. Щоб цього уникнути, кожна "зміна" буде відображена як мінімум на три події: одна запускається перед зміною, одна запускається під час зміни та одна запускається після її закінчення.
rwong

5

Ознайомтеся з правилами дизайну Microsoft , особливо одним із них:

CA1024: Використовуйте властивості, де це доречно

... Метод - це хороший кандидат стати власністю, якщо є одна з цих умов:

  • Не приймає аргументів і повертає інформацію про стан об'єкта.
  • Приймає єдиний аргумент для встановлення деякої частини стану об'єкта.

Властивості повинні вести себе так, ніби вони поля; якщо метод не може, його не слід змінювати на властивість. Методи кращі за властивості в наступних ситуаціях:

  • Метод виконує трудомістку операцію. Метод помітно повільніше, ніж час, необхідний для встановлення або отримання значення поля.
  • Метод виконує перетворення. Доступ до поля не повертає перетворену версію даних, які він зберігає.
  • Метод Get має помітний побічний ефект. Отримання значень поля не дає жодних побічних ефектів.
  • Порядок виконання важливий. Встановлення значення поля не залежить від виникнення інших операцій.
  • Виклик методу два рази поспіль створює різні результати.
  • Метод статичний, але повертає об'єкт, який може бути змінений абонентом. Отримання значення поля не дозволяє абоненту змінювати дані, які зберігаються в полі.
  • Метод повертає масив ...

2

Я вважаю, що властивості не повинні мати побічних ефектів. Однак є одне виняток із цього правила: ледаче завантаження. Якщо ви хочете ліниво завантажити щось у власність, це добре, тому що клієнт не може сказати, що ледача завантаження відбувається, і взагалі це не хвилює.

EDIT : О, ще одна річ - розгортання події, яка говорить "PropertyXYZ Changed!" (наприклад INotifyPropertyChanged) - добре на сетерах.


2

Окрім згаданих раніше ледачих навантажень, існує також функція реєстрації журналів. Це було б рідко, але це не викликає сумніву.


2

Абсолютів у програмуванні майже немає, для того, щоб відповісти на ваше запитання, нам потрібно переглянути деякі бажані властивості об'єктів і побачити, чи це щось стосується нашого випадку

  1. Ми хочемо, щоб заняття були легко перевірені.
  2. Ми хочемо, щоб класи підтримували паралельність
  3. Ми хочемо, щоб на заняттях було легко міркувати сторонніми особами

Якщо ми відповімо так, на деякі з цих питань, то, ймовірно, буде дуже ретельно подумати про властивості та їх побічні ефекти. Я припускаю, що, коли ви говорите побічні ефекти, ви маєте на увазі вторинні побічні ефекти, оскільки оновлення значення є самим побічним ефектом.

Чим більше побічних ефектів загалом, тим складніше буде перевірити клас. Чим більше вторинних побічних ефектів, тим важче буде подолати одночасність, але це важке питання, яке, мабуть, вимагає будь-якої великої дизайнерської думки в будь-якому випадку.

Мабуть, велика прихильність для людей, які ставляться до вашого класу як до «чорної скриньки», не було б легко зрозуміти, що якийсь стан змінився лише тому, що вони прочитали властивість, або що якась інша властивість змінюється через іншу зміну (яка може призвести до до каскадних оновлень).

Отже, загалом, ні, ми хочемо, щоб властивості були максимально простими в обґрунтуванні та прості, начебто це передбачається в проектному рішенні, інакше це був би метод. Хороший програміст завжди може порушувати правила, проте будьте впевнені, що у вас є справді поважна причина :)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.