Каковы преимущества пометки поля как «только для чтения» в C #? - PullRequest
249 голосов
/ 10 ноября 2008

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

Ответы [ 14 ]

170 голосов
/ 10 ноября 2008

Я не верю, что использование поля только для чтения приводит к увеличению производительности. Это просто проверка, чтобы убедиться, что после того, как объект полностью построен, это поле не может быть указано на новое значение.

Однако «только для чтения» очень отличается от других типов семантики только для чтения, потому что это обеспечивается во время выполнения CLR. Ключевое слово readonly компилируется в .initonly, что проверяется CLR.

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

Вот хорошая запись об одном из преимуществ неизменности: Threading

141 голосов
/ 10 ноября 2008

Ключевое слово readonly используется для объявления переменной-члена константой, но позволяет вычислять значение во время выполнения. Это отличается от константы, объявленной с модификатором const, значение которого должно быть установлено во время компиляции. Используя readonly, вы можете установить значение поля либо в объявлении, либо в конструкторе объекта, членом которого является поле.

Также используйте его, если вы не хотите перекомпилировать внешние DLL, которые ссылаются на константу (поскольку она заменяется во время компиляции).

59 голосов
/ 10 ноября 2008

Нет никаких очевидных преимуществ с точки зрения производительности при использовании readonly, по крайней мере, ни одно из тех, о которых я когда-либо видел, нигде не упоминалось. Это просто для того, чтобы делать то, что вы предлагаете, для предотвращения изменения после его инициализации.

Так что это выгодно тем, что помогает вам писать более надежный и читаемый код. Реальная выгода от таких вещей приходит, когда вы работаете в команде или на техобслуживании. Объявление чего-либо как readonly сродни заключению контракта на использование этой переменной в коде. Думайте об этом как о добавлении документации так же, как и о других ключевых словах, таких как internal или private, вы говорите «эта переменная не должна изменяться после инициализации», и, кроме того, вы применяете ее .

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

43 голосов
/ 23 ноября 2008

Если выразить это очень практично:

Если вы используете const в dll A и dll B ссылаются на этот const, значение этого const будет скомпилировано в dll B. Если вы переустановите dll A с новым значением для этого const, dll B все равно будет использовать Исходное значение.

Если вы используете только для чтения в dll A и dll B ссылки, которые только для чтения, то только для чтения всегда будет просматриваться во время выполнения. Это означает, что если вы повторно внедрите dll A с новым значением для этого только для чтения, dll B будет использовать это новое значение.

12 голосов
/ 25 мая 2011

Возможен случай, когда компилятор может оптимизировать производительность на основе наличия ключевого слова readonly.

Это применимо только в том случае, если поле только для чтения также помечено как static . В этом случае JIT-компилятор может предположить, что это статическое поле никогда не изменится. JIT-компилятор может учитывать это при компиляции методов класса.

Типичный пример: ваш класс может иметь статическое поле только для чтения IsDebugLoggingEnabled , которое инициализируется в конструкторе (например, на основе файла конфигурации). После того, как фактические методы скомпилированы JIT, компилятор может пропустить целые части кода, когда ведение журнала отладки не включено.

Я не проверял, реализована ли эта оптимизация в текущей версии JIT-компилятора, так что это всего лишь предположение.

6 голосов
/ 23 ноября 2008

Помните, что чтение только относится только к самому значению, поэтому, если вы используете ссылочный тип, только чтение только защищает ссылку от изменения. Состояние экземпляра не защищено только для чтения.

5 голосов
/ 24 января 2013

Не забывайте, что есть обходной путь, чтобы получить поля readonly, установленные вне конструкторов, используя параметры out.

Немного грязно, но:

private readonly int _someNumber;
private readonly string _someText;

public MyClass(int someNumber) : this(data, null)
{ }

public MyClass(int someNumber, string someText)
{
    Initialise(out _someNumber, someNumber, out _someText, someText);
}

private void Initialise(out int _someNumber, int someNumber, out string _someText, string someText)
{
    //some logic
}

Дальнейшее обсуждение здесь: http://www.adamjamesnaylor.com/2013/01/23/Setting-Readonly-Fields-From-Chained-Constructors.aspx

2 голосов
/ 17 июня 2018

Добавление базового аспекта для ответа на этот вопрос:

Свойства могут быть выражены только для чтения, пропуская оператор set. Поэтому в большинстве случаев вам не нужно добавлять ключевое слово readonly в свойства:

public int Foo { get; }  // a readonly property

В отличие от этого: Полям необходимо ключевое слово readonly для достижения аналогичного эффекта:

public readonly int Foo; // a readonly field

Таким образом, одним из преимуществ пометки поля как readonly может быть достижение такого же уровня защиты от записи, как у свойства без оператора set - без необходимости менять поле на свойство, если по какой-либо причине желательно.

2 голосов
/ 28 июня 2016

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

1 голос
/ 29 апреля 2018

Удивительно, но чтение только на самом деле может привести к замедлению кода, как обнаружил Джон Скит при тестировании своей библиотеки Noda Time. В этом случае тест, выполненный за 20 секунд, занял всего 4 секунды после удаления только для чтения.

https://codeblog.jonskeet.uk/2014/07/16/micro-optimization-the-surprising-inefficiency-of-readonly-fields/

...