Чи гарантування незмінності є виправданням для відкриття поля замість властивості?


10

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

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

Тож чи гарантування незмінності є законною причиною вибору readonlyполя над властивістю в деяких випадках?

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



1
@gnat Просто для уточнення, використовуючи "Властивість ReadOnly", вони використовують термінологію VB.NET, що означає властивість без сеттера, а не поле для читання. Для мого запитання два варіанти, з якими порівнює це питання, є рівнозначними - вони обидва використовують властивості.
Бен Аронсон

Відповіді:


6

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

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

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

Неможливість змінити "незмінність" члена без зміни загальнодоступного інтерфейсу класу не є великим бар'єром у цьому випадку. Зазвичай, якщо ви хочете змінити загальнодоступне поле у ​​власність, воно проходить гладко, за винятком двох випадків, з якими ви маєте мати справу:

  1. Все, що пише члену цього об'єкта, як-от: object.Location.X = 4можливо, лише якщо Locationце поле. Це не стосується для поля лише для читання, оскільки ви не можете змінити структуру для читання тільки. (Це припущення Location, що це тип значення - якщо ні, то ця проблема все одно не застосовуватиметься, оскільки readonlyне захищає від подібних речей.)
  2. Будь-який виклик методу, який передає значення як outабо ref. Знову ж , це не дуже актуально для тільки для читання поля, так outі refпараметри , як правило, модифікуються, і це помилка компілятора в будь-якому випадку передати значення тільки для читання , як з або реф.

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

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


З інтересу, чому є відкриті статичні читання - полів в порядку? Це лише тому, що виключення методів екземплярів видаляє більшість випадків використання властивостей на незмінних полях (наприклад, підрахунок ударів, ледачий навантаження тощо)?
Бен Аронсон

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

2

З мого досвіду, readonlyключове слово - це не та магія, яку він обіцяє.

Наприклад, у вас є клас, де конструктор був простим. Зважаючи на використання (і те, що деякі властивості / поля класу незмінні після побудови), ви можете подумати, що це не має значення, тому ви використовували readonlyключове слово.

Пізніше клас стає більш складним, так само і його конструктор. (Скажімо, це відбувається в проекті, який є експериментальним або має досить високу швидкість, що зменшує масштаб / контроль, але потрібно змусити його якось працювати.) Ви виявите, що поля, які readonlyможна змінити лише всередині конструктора - ви не може змінити його в методах, навіть якщо цей метод викликається лише конструктором.

Це технічне обмеження було визнано дизайнерами C # і може бути зафіксовано в наступній версії. Але поки це не виправлено, використання readonlyбільш обмежувальне і може заохочувати поганий стиль кодування (вкладаючи все в метод конструктора).

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


1
Я вважаю за краще , щоб конструктори просто - см brendan.enrick.com/post / ... , blog.ploeh.dk/2011/03/03/InjectionConstructorsshouldbesimple так насправді, тільки для читання сприяє хорошому стилю кодування
AlexFoxGill

1
Конструктор може передавати readonlyполе в якості outабо refпараметра до інших методів, які потім будуть вільно змінювати його на свій розсуд.
supercat

1

Поля - ЗАВЖДИ деталі реалізації. Ви не хочете розкривати деталі своєї реалізації.

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

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

Тільки тому, що ми можемо щось зробити з мовою, не означає, що ми повинні щось робити з мовою.

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


1
Я розумію важливість розбудови майбутньої гнучкості, але, безумовно, якщо я напишу такий клас, як, скажімо, Rationalз громадськістю, незмінним Numeratorта Denominatorчленам, я можу бути надзвичайно впевнений, що цим членам не потрібна ледача завантаження чи лічильник хітів або схожий?
Бен Аронсон

Але чи можете ви бути впевнені, що впровадження, використовуючи floatтипи для Numeratorі Denominatorне потрібно буде їх змінювати doubles?
Стівен

1
Ну, е, ні, вони точно не повинні бути ні floatні double. Вони повинні бути int, longабо BigInteger. Можливо, їх потрібно змінити ... але якщо так, то їм також потрібно змінити публічний інтерфейс, тому дійсно використання властивостей не додає інкапсуляції навколо цієї деталі.
Бен Аронсон

-3

Ні. Це не виправдання.

Використання readonly як гарантії незмінності, а не виправдання, більше схоже на хак.

Readonly означає, що значення призначається конструктором o при визначенні змінної.

Я думаю, це буде поганою практикою

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

Простий приклад інтерфейсу: введіть тут опис зображення


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