В чем преимущества маркировки поля как `readonly` в С#?

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

Ответ 1

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

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

Ответ 2

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

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

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

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

Ответ 3

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

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

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

Ответ 4

Чтобы это было очень практично:

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

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

Ответ 5

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

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

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

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

Ответ 6

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

Ответ 7

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

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

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

Ответ 8

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

Ответ 9

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

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

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

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

public readonly int Foo; // a readonly field

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

Ответ 10

Будьте осторожны с частными массивами readonly. Если они подвергаются клиенту как объекту (вы можете сделать это для COM-взаимодействия, как и я), клиент может манипулировать значениями массива. Используйте метод Clone() при возврате массива в качестве объекта.

Ответ 11

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

code https://repl.it/HvRU/1

using System;

class MainClass {
    public static void Main (string[] args) {

        Console.WriteLine(new Test().c);
        Console.WriteLine(new Test("Constructor").c);
        Console.WriteLine(new Test().ChangeC()); //Error A readonly field 
        // `MainClass.Test.c' cannot be assigned to (except in a constructor or a 
        // variable initializer)
    }


    public class Test {
        public readonly string c = "Hello World";
        public Test() {

        }

        public Test(string val) {
          c = val;
        }

        public string ChangeC() {
            c = "Method";
            return c ;
        }
    }
}

Ответ 12

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

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

Ответ 13

В WPF может быть преимущество в производительности, поскольку оно устраняет необходимость в дорогостоящих DependencyProperties. Это может быть особенно полезно для коллекций

Ответ 14

Еще одна интересная часть использования маркировки только для чтения может защищать поле от инициализации в singleton.

например, в коде csharpindepth:

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy =
        new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance { get { return lazy.Value; } }

    private Singleton()
    {
    }
}

readonly играет небольшую роль, защищая поле Singleton от инициализации дважды. Еще одна деталь заключается в том, что для упомянутого сценария вы не можете использовать const, потому что const заставляет создавать во время компиляции, но singleton делает создание во время выполнения.