Еще в 2009 году этот совет выглядел просто как восторг от сорта , который сдвинул мой сыр . Сегодня это почти смехотворно устарело.
Один очень важный момент, который, кажется, намекает на многие ответы, но не совсем решает проблему, заключается в том, что эти "опасности" свойств являются преднамеренной частью структуры фреймворка!
Да, свойства могут:
Укажите разные модификаторы доступа для геттера и сеттера. Это преимущество над полями. Распространенным примером является наличие общедоступного геттера и защищенного или внутреннего сеттера, очень полезного метода наследования, который не может быть достигнут только одними полями.
Брось исключение. На сегодняшний день это остается одним из наиболее эффективных методов проверки, особенно при работе со структурами пользовательского интерфейса, в которых используются концепции привязки данных. Гораздо сложнее убедиться, что объект остается в допустимом состоянии при работе с полями.
Выполните много времени. Действительное сравнение здесь с методами , которые занимают одинаково много времени - не полями . Нет никаких оснований для утверждения «метод предпочтителен», кроме личных предпочтений одного автора.
Возвращать различные значения из его получателя при последующих выполнениях. Это почти похоже на шутку в такой непосредственной близости к точке, превозносящей достоинства ref
/ out
параметров с полями, чье значение поля после вызова ref
/ out
почти гарантированно будет другим от его предыдущего значения, и непредсказуемо так.
Если мы говорим о конкретном (и практически академическом) случае однопоточного доступа без афферентных связей, довольно хорошо понято , что просто плохой дизайн свойства иметь изменение видимого состояния побочные эффекты, и, возможно, моя память угасает, но я просто не могу вспомнить какие-либо примеры людей, использующих DateTime.Now
и ожидающих, что одно и то же значение будет появляться каждый раз. По крайней мере, ни в одном случае, если бы они не испортили это так же плохо с гипотетическим DateTime.Now()
.
Вызывает наблюдаемые побочные эффекты - что, разумеется, и является причиной того, что свойства изначально были изобретены как языковая функция. Собственные рекомендации Microsoft Property Design указывают на то, что порядок установки не должен иметь значения, так как в противном случае подразумевается временная связь . Конечно, вы не можете добиться временной связи только с полями, но это только потому, что вы не можете заставить какое-либо осмысленное поведение вообще происходить только с полями, пока не будет выполнен какой-либо метод.
Средства доступа к свойствам могут на самом деле помочь предотвратить определенные типы временных связей, переведя объект в допустимое состояние перед выполнением какого-либо действия - например, если класс имеет StartDate
и EndDate
затем настройте EndDate
до того, как StartDate
также может заставить StartDate
вернуться назад. Это верно даже в многопоточных или асинхронных средах, включая очевидный пример пользовательского интерфейса, управляемого событиями.
Другие вещи, которые могут делать свойства, которые не могут содержать поля:
- Ленивая загрузка , один из наиболее эффективных способов предотвращения ошибок порядка инициализации.
- Уведомления об изменениях , которые в значительной степени составляют основу MVVM архитектуры.
- Наследование , например, определение абстрактного
Type
или Name
, чтобы производные классы могли предоставлять интересные, но, тем не менее, постоянные метаданные о себе.
- Перехват , благодаря вышесказанному.
- Индексаторы , которые каждый, кому когда-либо приходилось работать с COM-взаимодействием и неизбежным выбросом
Item(i)
вызовов, признает замечательную вещь.
- Работа с PropertyDescriptor , который необходим для создания дизайнеров и для сред XAML в целом.
Рихтер явно плодовитый автор и много знает о CLR и C #, но я должен сказать, что, похоже, он изначально написал этот совет (я не уверен, что он в его более поздних ревизиях - я искренне надеюсь, нет) что он просто не хотел расставаться со старыми привычками и испытывал трудности с принятием соглашений C # (например, против C ++).
Под этим я подразумеваю, что его аргумент «свойства считаются вредными» сводится к одному утверждению: Свойства выглядят как поля, но они могут не действовать как поля. И проблема с утверждением это не правда или, в лучшем случае, вводит в заблуждение. Свойства не выглядят как поля - по крайней мере, они не должны выглядеть как поля.
Существует два очень строгих соглашения о кодировании в C # с аналогичными соглашениями, общими для других языков CLR, и FXCop будет кричать на вас, если вы не будете следовать им:
- Поля должны всегда быть частными, никогда общедоступными.
- Поля должны быть объявлены в camelCase. Свойства являются PascalCase.
Таким образом, нет никакой двусмысленности относительно того, является ли Foo.Bar = 42
средством доступа к свойству или средством доступа к полю. Это средство доступа к свойству, и его следует рассматривать как любой другой метод - он может быть медленным, он может вызвать исключение и т. Д. Такова природа Абстракция - это полностью зависит от того, как объявить класс, как реагировать. Дизайнеры классов должны применять принцип наименьшего удивления, но посетители не должны предполагать ничего о свойстве, кроме того, что оно делает то, что говорит на жестяной банке. Это специально.
Альтернативой свойствам являются методы получения / установки везде. Это Java-подход, и он был спорным с самого начала . Хорошо, если это ваша сумка, но это не то, как мы катимся в. Мы стараемся, по крайней мере в рамках статически типизированной системы, избегать того, что Фаулер называет синтаксическим шумом . Нам не нужны дополнительные скобки, дополнительные get
/ set
бородавки или дополнительные сигнатуры методов - если мы не сможем избежать их без потери ясности.
Скажи, что хочешь, но foo.Bar.Baz = quux.Answers[42]
всегда будет намного легче читать, чем foo.getBar().setBaz(quux.getAnswers().getItem(42))
. И когда вы читаете тысячи строк этого в день, это имеет значение.
(И если ваш естественный ответ на вышеприведенный абзац состоит в том, чтобы сказать: «Конечно, это трудно читать, но было бы легче, если бы вы разбили его на несколько строк», тогда мне жаль говорить, что Вы полностью пропустили точку.)