Общее руководство по C # - всегда использовать свойство над открытым полем. Это имеет смысл - выставляя поле, вы раскрываете много деталей реализации. Со свойством вы инкапсулируете эту деталь, чтобы она была скрыта от потребления кода, а изменения реализации отделены от изменений интерфейса.
Однако мне интересно, есть ли иногда допустимое исключение из этого правила при работе с readonly
ключевым словом. Применяя это ключевое слово к общедоступному полю, вы даете дополнительную гарантию: неизменность. Это не просто деталь реализации, потребитель может заинтересовать неизменность. Использование readonly
поля делает его частью публичного контракта, и это не может быть нарушено будущими изменениями или наследованием без необходимости изменять публичный интерфейс. Это то, что недвижимость не может предложить.
Так является ли гарантия неизменности законной причиной выбора readonly
поля над свойством в некоторых случаях?
(Для пояснения, я, конечно же, не говорю, что вы всегда должны делать этот выбор только потому, что поле оказывается неизменным в данный момент, только когда оно имеет смысл как часть дизайна класса и предназначено для включения неизменности в его контракт. В основном меня интересуют ответы, посвященные тому, может ли это быть оправдано, а не конкретным случаям, в которых это не так, например, когда вам нужно, чтобы участник был включен interface
, или хотите выполнять ленивую загрузку.)
источник
Ответы:
Публичные статические поля только для чтения, конечно, в порядке. Они рекомендуются для определенных ситуаций, например, когда вам нужен именованный постоянный объект, но вы не можете использовать
const
ключевое слово.Поля, доступные только для чтения, немного сложнее. Нет большого преимущества, получаемого над свойством без открытого установщика, и есть главный недостаток: поля не могут быть членами интерфейсов. Поэтому, если вы решите провести рефакторинг своего кода для работы с интерфейсом, а не с конкретным типом, вам придется изменить поля, доступные только для чтения, на свойства с общедоступными получателями.
Открытое не виртуальное свойство без защищенного установщика обеспечивает защиту от наследования, если только унаследованные классы не имеют доступа к вспомогательному полю. Вы можете полностью реализовать этот контракт в базовом классе.
Неспособность изменить «неизменность» члена без изменения открытого интерфейса класса в этом случае не является серьезным препятствием. Обычно, если вы хотите изменить открытое поле на свойство, все идет гладко, за исключением того, что вам нужно разобраться в двух случаях:
object.Location.X = 4
возможно, только еслиLocation
это поле. Это не относится к полю только для чтения, потому что вы не можете изменить структуру только для чтения. (Предполагается,Location
что это тип значения - в противном случае эта проблема не будет применяться в любом случае, потомуreadonly
что не защищает от подобных вещей.)out
илиref
. Опять же, это не очень важно для поля только для чтения, потому чтоout
иref
параметры имеют тенденцию изменяться, и это ошибка компилятора в любом случае передавать значение только для чтения как out или ref.Другими словами, хотя преобразование поля «только для чтения» в свойство «только для чтения» нарушает двоичную совместимость, другой исходный код необходимо будет перекомпилировать без каких-либо изменений, чтобы обработать это изменение. Это делает гарантию действительно легко сломанной.
Я не вижу каких-либо преимуществ для области
readonly
членства, и невозможность иметь подобные вещи на интерфейсе является для меня недостатком, так как я бы не стал им пользоваться.источник
По моему опыту,
readonly
ключевое слово - это не та магия, которой оно обещает.Например, у вас есть класс, где конструктор был простым. Учитывая использование (и тот факт, что некоторые свойства / поля класса являются неизменными после создания), вы можете подумать, что это не имеет значения, поэтому вы использовали
readonly
ключевое слово.Позже класс становится более сложным, как и его конструктор. (Допустим, это происходит в проекте, который либо экспериментален, либо имеет достаточно высокую скорость масштабирования из области видимости / элемента управления, но вам нужно как-то заставить его работать.) Вы обнаружите, что поля, которые
readonly
могут быть изменены только внутри конструктора - вы не может изменить его в методах, даже если этот метод вызывается только из конструктора.Это техническое ограничение было признано разработчиками C # и может быть исправлено в будущей версии. Но до тех пор, пока это не будет исправлено, использование
readonly
более ограничено и может поощрить некоторый плохой стиль кодирования (помещая все в метод конструктора).Напомним, что ни
readonly
свойство non-public-setter не дает никакой гарантии «полностью инициализированного объекта». Другими словами, именно разработчик класса решает, что означает «полностью инициализированный» и как защитить его от непреднамеренного использования, и такая защита, как правило, не является надежной, что означает, что кто-то может найти способ обойти это.источник
readonly
поле в качестве параметраout
илиref
в другие методы, которые затем могут свободно изменять его по своему усмотрению.Поля являются ВСЕГДА деталями реализации. Вы не хотите раскрывать детали своей реализации.
Фундаментальный принцип разработки программного обеспечения заключается в том, что мы не знаем, какими будут требования в будущем. Хотя ваша
readonly
сфера может быть хорошей сегодня, нет ничего, чтобы предполагать, что она будет частью требований завтрашнего дня. Что, если завтра потребуется реализовать счетчик посещений, чтобы определить, сколько раз к этому полю обращаются? Что если вам нужно разрешить подклассам переопределять поле?Свойства настолько просты в использовании и намного более гибки, чем открытые поля, что я не могу представить ни одного варианта использования, когда я выбрал бы c # в качестве языка и использовал бы открытые только для чтения поля в классе. Если бы производительность была настолько низкой, что я не смог бы принять удар дополнительного вызова метода, я бы, вероятно, использовал другой язык.
То, что мы можем что-то делать с языком, не означает, что мы должны что-то делать с языком.
Мне действительно интересно, сожалеют ли разработчики языка о том, что поля могут быть обнародованы. Я не могу вспомнить, когда в последний раз я даже рассматривал использование неконстантных открытых полей в своем коде.
источник
Rational
с public, неизменяемымиNumerator
иDenominator
членами, я могу быть чрезвычайно уверен, что этим участникам не потребуется ленивая загрузка или счетчик обращений или аналогичный?float
типы дляNumerator
иDenominator
, не нуждается в измененииdoubles
?float
ниdouble
. Они должны бытьint
,long
илиBigInteger
. Может быть, те должны измениться ... но если это так, то они должны измениться и в публичном интерфейсе, поэтому использование свойств не добавляет инкапсуляции вокруг этой детали.Нет, это не оправдание.
Использование readonly в качестве гарантии неизменности, а не оправдания, больше похоже на взлом.
Только чтение означает, что значение присваивается конструктором o при определении переменной.
Я думаю, что это будет плохая практика
Если вы действительно хотите гарантировать неизменность, используйте интерфейс, у которого больше плюсов, чем минусов.
Пример простого интерфейса:
источник