Які переваги для позначення поля як "лише для читання" в C #?


295

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


8
Хороша зовнішня відповідь: dotnetperls.com/readonly
OneWorld

3
Цікаво. Це, по суті, еквівалент C # цього питання щодо Java stackoverflow.com/questions/137868/… Тут обговорення набагато менше нагрівається, хоча ... хм ...
RAY

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

2
більше про покарання за ефективність: codeblog.jonskeet.uk/2014/07/16/…
CAD

Відповіді:


171

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

Також використовуйте його, якщо вам не потрібно перекомпілювати зовнішні DLL-файли, на які посилається константа (оскільки вона замінюється під час компіляції).


193

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

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

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

Ось хороший запис про одну з переваг непорушності: Нитка


6
Якщо ви читаєте це, stackoverflow.com/questions/9860595/… лише член , який читає, може бути змінений і виглядає як непослідовна поведінка .net
Акаш Кава,

69

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

Тож це вигідно тим, що допомагає писати більш надійний і читабельний код. Справжня користь таких речей приходить, коли ви працюєте в команді чи обслуговуєте. Декларувати щось readonlyтаке, що схоже на додавання контракту на використання цієї змінної в код. Подумайте про це як додавання документації так само, як і інші ключові слова, наприклад, internalабо private, ви говорите, "цю змінну не слід змінювати після ініціалізації", і тим більше ви її застосовуєте .

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


І отох дезінфікує мову. Однак не заперечуючи заяву про виплату.
декрет

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

7
Ця відповідь та обговорення є фактично найкращою відповіддю на мій погляд +1
Джефф Мартін

@Xiaofu: Ви змусили мене подумати про те, щоб прочитати лише хахаха, прекрасне пояснення того, що ніхто в цьому світі не міг би пояснити найрозумнішого розуму
Учень

Тобто ви залишаєтесь у своєму коді, що це значення не повинно змінюватися в будь-який час.
Андез

51

Слід сказати дуже практично:

Якщо ви використовуєте const в посиланнях dll A і dll B, які const, значення цього const буде зібрано в dll B. Якщо ви переустановите dll A з новим значенням для цього const, dll B все одно буде використовувати початкове значення.

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


4
Це хороший практичний приклад, щоб зрозуміти різницю. Дякую.
Шию

З іншого боку, constможе бути надбавка в продуктивності readonly. Ось трохи глибше пояснення з кодом: dotnetperls.com/readonly
Dio Phung

2
Я думаю, що в цій відповіді відсутній найбільший практичний термін: можливість зберігати обчислювані під час виконання значення в readonlyполя. Ви не можете зберігати new object();в a constі це має сенс, тому що ви не можете пекти цінні речі, такі як посилання на інші збірки, під час компіляції без зміни ідентичності.
бінкі

14

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

Це застосовується лише в тому випадку, якщо поле для читання лише позначене як статичне . У цьому випадку компілятор JIT може припустити, що це статичне поле ніколи не зміниться. Компілятор JIT може враховувати це при складанні методів класу.

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

Я не перевіряв, чи реально ця оптимізація реалізована в поточній версії компілятора JIT, тому це лише спекуляція.


чи є джерело для цього?
Седат Капаноглу

4
Поточний компілятор JIT насправді реалізує це, і з CLR 3.5. github.com/dotnet/coreclr/isissue/1079
mirhagk

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

9

Майте на увазі, що readonly застосовується лише до самого значення, тому якщо ви використовуєте тип посилання, readonly лише захищає посилання від зміни. Стан екземпляра не захищається лише шляхом читання.


4

Не забувайте, що існує рішення, щоб отримати 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


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

Це навіть не намагається відповісти на питання.
Шерідан

2

Дивно, але лише читання може насправді призвести до уповільнення коду, як виявив Джон Скіт при тестуванні своєї бібліотеки Noda Time. У цьому випадку тест, який запустився за 20 секунд, зайняв лише 4 секунди після видалення лише заново.

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


3
Зауважте, що якщо поле типу типу, яке є readonly structв C # 7.2, користь від того, щоб зробити поле не зчитуваним, зникає.
Джон Скіт

1

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


1

Додайте базовий аспект, щоб відповісти на це запитання:

Властивості можна виразити як прочитані лише за допомогою виключення setоператора. Тому в більшості випадків вам не потрібно буде додавати readonlyключові слова до властивостей:

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

На відміну від цього: полям потрібне readonlyключове слово, щоб досягти подібного ефекту:

public readonly int Foo; // a readonly field

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


Чи є різниця в поведінці між двома?
petrosmm

0

Будьте обережні з приватними масивами лише для читання. Якщо вони виявляються клієнтом як об'єктом (ви можете зробити це для взаємодії COM, як я), клієнт може маніпулювати значеннями масиву. Використовуйте метод Clone (), повертаючи масив як об'єкт.


20
Ні; виставити ReadOnlyCollection<T>замість масиву.
SLaks

Це повинен бути коментар, а не відповідь, оскільки він не дає відповіді на питання ...
Пітер

Як не дивно, мені сказали поставити подібні речі як відповідь, а не коментар, коли я це робив на іншій посаді минулого тижня.
Кайл Баран

Станом на 2013 рік, ви можете використовувати ImmutableArray<T>, що дозволяє уникнути боксу на інтерфейс ( IReadOnlyList<T>) або перетворення в клас ( ReadOnlyCollection). Він має продуктивність, порівнянну з рідними масивами: blogs.msdn.microsoft.com/dotnet/2013/06/24/…
Чарльз Тейлор

0

У WPF може бути користь від продуктивності, оскільки вона усуває потребу у дорогих DependencyProperties. Це може бути особливо корисно для колекцій


0

Ще однією цікавою частиною використання маркування для читання може бути захист поля від ініціалізації в одиночному.

наприклад в коді з 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 примушує створювати під час компіляції, але синглтон створює створення під час виконання.


0

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

код 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 ;
        }
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.