Quali sono i vantaggi di contrassegnare un campo come "di sola lettura" in C #?


295

Quali sono i vantaggi di avere una variabile membro dichiarata come sola lettura? Sta solo proteggendo da qualcuno che cambia il suo valore durante il ciclo di vita della classe o l'utilizzo di questa parola chiave comporta miglioramenti della velocità o dell'efficienza?


8
Buona risposta esterna: dotnetperls.com/readonly
OneWorld,

3
Interessante. Questo è essenzialmente l'equivalente C # di questa domanda Java stackoverflow.com/questions/137868/… La discussione qui è molto meno accesa sebbene ... hmm ...
RAY

7
Vale la pena notare che i readonlycampi di tipi di struttura impongono una penalità di prestazione rispetto ai campi mutabili che non sono semplicemente mutati, poiché l'invocazione di qualsiasi membro di un readonlycampo di tipo valore farà sì che il compilatore faccia una copia del campo e invochi il membro su quello.
supercat,

2
altro sulla penalità di prestazione: codeblog.jonskeet.uk/2014/07/16/…
CAD bloke

Risposte:


171

La readonlyparola chiave viene utilizzata per dichiarare una variabile membro una costante, ma consente di calcolare il valore in fase di esecuzione. Ciò differisce da una costante dichiarata con il constmodificatore, che deve avere il valore impostato al momento della compilazione. Usando readonlypuoi impostare il valore del campo nella dichiarazione o nel costruttore dell'oggetto di cui fa parte il campo.

Usalo anche se non vuoi ricompilare DLL esterne che fanno riferimento alla costante (poiché viene sostituita al momento della compilazione).


193

Non credo ci siano miglioramenti delle prestazioni usando un campo di sola lettura. È semplicemente un controllo per garantire che una volta che l'oggetto è stato completamente costruito, quel campo non può essere puntato su un nuovo valore.

Tuttavia, "sola lettura" è molto diverso dagli altri tipi di semantica di sola lettura perché è imposto in fase di esecuzione dal CLR. La parola chiave readonly viene compilata fino a .initonly che è verificabile dal CLR.

Il vero vantaggio di questa parola chiave è generare strutture di dati immutabili. Le strutture di dati immutabili per definizione non possono essere modificate una volta costruite. Questo rende molto facile ragionare sul comportamento di una struttura in fase di esecuzione. Ad esempio, non c'è pericolo di passare una struttura immutabile a un'altra porzione casuale di codice. Non possono mai cambiarlo in modo da poter programmare in modo affidabile contro quella struttura.

Ecco una buona voce su uno dei vantaggi dell'immutabilità: il threading


6
Se leggi questo, stackoverflow.com/questions/9860595/… il membro di sola lettura può essere modificato e sembra un comportamento incoerente di .net
Akash Kava

Puoi aggiornare il link al post su Threading sopra? È rotto.
Akshay Khot

69

Non ci sono evidenti benefici in termini di prestazioni nell'uso readonly, almeno nessuno di quelli che abbia mai visto menzionato da nessuna parte. È solo per fare esattamente come suggerisci, per impedire la modifica una volta che è stata inizializzata.

Quindi è utile in quanto ti aiuta a scrivere codice più robusto, più leggibile. Il vero vantaggio di cose come questa viene quando lavori in gruppo o per manutenzione. Dichiarare qualcosa come readonlyè come mettere un contratto per l'uso di quella variabile nel codice. Pensala come aggiungere documentazione allo stesso modo di altre parole chiave come internalo private, stai dicendo "questa variabile non deve essere modificata dopo l'inizializzazione" e inoltre la stai applicando .

Quindi, se crei una classe e contrassegni alcune variabili membro in readonlybase alla progettazione, impedisci a te stesso o ad un altro membro del team di fare un errore in seguito quando si espandono o modificano la tua classe. Secondo me, questo è un vantaggio che vale la pena avere (a scapito della complessità linguistica aggiuntiva come fa doofledorfer nei commenti).


E otoh semplifica la lingua. Non negare la tua dichiarazione di indennità, tuttavia.
dkretz,

3
Sono d'accordo, ma suppongo che il vero vantaggio arrivi quando più di una persona sta lavorando al codice. È come avere una piccola dichiarazione di design all'interno del codice, un contratto per il suo utilizzo. Probabilmente dovrei inserirlo nella risposta, hehe.
Xiaofu,

7
Questa risposta e discussione è in realtà la migliore risposta secondo me +1
Jeff Martin

@Xiaofu: Mi hai reso costante l'idea di avere ahahah una bella spiegazione che nessuno in questo mondo potrebbe spiegare la mente più sciocca per capire
Apprendimento

Vale a dire che rimani intenzionato nel tuo codice che questo valore non dovrebbe cambiare in nessun momento.
Andez,

51

Per dirla in termini molto pratici:

Se usi una const in dll A e dll B fa riferimento a quella const, il valore di quella const verrà compilato in dll B. Se ridistribuisci dll A con un nuovo valore per quella const, la dll B continuerà a utilizzare il valore originale.

Se si utilizza un readonly nei riferimenti dll A e dll B di sola lettura, quello di sola lettura verrà sempre cercato in fase di esecuzione. Ciò significa che se ridistribuisci la dll A con un nuovo valore per quello di sola lettura, la dll B utilizzerà quel nuovo valore.


4
Questo è un buon esempio pratico per capire la differenza. Grazie.
Shyju,

D'altra parte, constpotrebbe avere un miglioramento delle prestazioni readonly. Ecco una spiegazione un po 'più profonda con il codice: dotnetperls.com/readonly
Dio Phung

2
Penso che manchi il termine pratico più grande da questa risposta: la capacità di archiviare i valori calcolati durante l'esecuzione in readonlycampi. Non è possibile archiviare a new object();in a conste questo ha senso perché non è possibile eseguire operazioni non di valore come riferimenti in altri assiemi durante il tempo di compilazione senza cambiare identità.
binki,

14

Esiste un potenziale caso in cui il compilatore può eseguire un'ottimizzazione delle prestazioni in base alla presenza della parola chiave sola lettura.

Questo vale solo se il campo di sola lettura è anche contrassegnato come statico . In tal caso, il compilatore JIT può presumere che questo campo statico non cambierà mai. Il compilatore JIT può tenerne conto durante la compilazione dei metodi della classe.

Esempio tipico: la tua classe potrebbe avere un campo IsDebugLoggingEnabled di sola lettura che viene inizializzato nel costruttore (ad esempio basato su un file di configurazione). Una volta compilati i metodi effettivi JIT, il compilatore può inserire intere parti del codice quando la registrazione di debug non è abilitata.

Non ho verificato se questa ottimizzazione è effettivamente implementata nella versione corrente del compilatore JIT, quindi questa è solo una speculazione.


c'è una fonte per questo?
Sedat Kapanoglu,

4
L'attuale compilatore JIT infatti lo implementa, e da allora CLR 3.5. github.com/dotnet/coreclr/issues/1079
mirhagk,

Non è possibile eseguire ottimizzazioni sui campi di sola lettura per il semplice motivo che i campi di sola lettura non sono di sola lettura ma di lettura / scrittura. Sono solo un suggerimento del compilatore che la maggior parte dei compilatori rispetta e che i valori dei campi di sola lettura possono essere facilmente sovrascritti attraverso la riflessione (anche se non in un codice parzialmente attendibile).
Christoph

9

Ricorda che si applica in sola lettura al valore stesso, quindi se si utilizza un tipo di riferimento in sola lettura protegge solo il riferimento dalla modifica. Lo stato dell'istanza non è protetto da sola lettura.


4

Non dimenticare che esiste una soluzione alternativa per readonlyimpostare i campi all'esterno di qualsiasi costruttore utilizzando outparams.

Un po 'disordinato ma:

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
}

Ulteriori discussioni qui: http://www.adamjamesnaylor.com/2013/01/23/Setting-Readonly-Fields-From-Chained-Constructors.aspx


4
I campi sono ancora assegnati nel costruttore .. non c'è "aggiramento" che. Non importa se i valori provengono da singole espressioni, da un tipo complesso decomposto o assegnati tramite chiamata dalla semantica di riferimento di out..
user2864740

Questo non tenta nemmeno di rispondere alla domanda.
Sheridan,

2

Sorprendentemente, in sola lettura può effettivamente portare a un codice più lento, come Jon Skeet ha scoperto durante il test della sua libreria Noda Time. In questo caso, un test eseguito in 20 secondi ha richiesto solo 4 secondi dopo la rimozione di sola lettura.

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


3
Si noti che se il campo è di un tipo che è readonly structin C # 7.2, il vantaggio di rendere il campo non di sola lettura scompare.
Jon Skeet,

1

Se hai un valore pre-definito o pre-calcolato che deve rimanere lo stesso per tutto il programma, allora dovresti usare costante ma se hai un valore che deve essere fornito in fase di esecuzione ma una volta assegnato dovrebbe rimanere lo stesso per tutto il programma, dovresti usare sola lettura. ad esempio, se è necessario assegnare l'ora di inizio del programma o se si deve memorizzare un valore fornito dall'utente all'inizializzazione dell'oggetto e si deve limitarlo da ulteriori modifiche che è necessario utilizzare in sola lettura.


1

Aggiunta di un aspetto di base per rispondere a questa domanda:

Le proprietà possono essere espresse in sola lettura tralasciando l' setoperatore. Quindi nella maggior parte dei casi non sarà necessario aggiungere la readonlyparola chiave alle proprietà:

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

Al contrario, i campi richiedono la readonlyparola chiave per ottenere un effetto simile:

public readonly int Foo; // a readonly field

Quindi, uno dei vantaggi della marcatura di un campo readonlypuò essere quello di raggiungere un livello di protezione dalla scrittura simile a una proprietà senza setoperatore - senza dover cambiare il campo in una proprietà, se per qualsiasi motivo, ciò è desiderato.


C'è una differenza nel comportamento tra i 2?
petrosmm,

0

Fare attenzione con array di sola lettura privati. Se questi vengono esposti a un client come oggetto (potresti farlo per l'interoperabilità COM come ho fatto io) il client può manipolare i valori dell'array. Utilizzare il metodo Clone () quando si restituisce un array come oggetto.


20
No; esporre un ReadOnlyCollection<T>invece di un array.
SLaks

Questo dovrebbe essere un commento non una risposta in quanto non fornisce alcuna risposta alla domanda ...
Peter,

Stranamente, mi è stato detto di mettere questo genere di cose come una risposta, piuttosto che un commento quando l'ho fatto su un altro post la scorsa settimana.
Kyle Baran,

A partire dal 2013, è possibile utilizzare ImmutableArray<T>, il che evita l'inscatolamento in un'interfaccia ( IReadOnlyList<T>) o il wrapping in una classe ( ReadOnlyCollection). Ha prestazioni paragonabili agli array nativi: blogs.msdn.microsoft.com/dotnet/2013/06/24/…
Charles Taylor

0

Ci può essere un vantaggio in termini di prestazioni in WPF, in quanto elimina la necessità di costose DependencyProperties. Questo può essere particolarmente utile con le raccolte


0

Un'altra parte interessante dell'uso della marcatura di sola lettura può essere la protezione del campo dall'inizializzazione in singleton.

ad esempio nel codice di 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()
    {
    }
}

svolge solo un piccolo ruolo di protezione del campo Singleton dall'inizializzazione due volte. Un altro dettaglio è che per lo scenario menzionato non è possibile utilizzare const perché const forza la creazione durante la compilazione, ma singleton esegue la creazione in fase di esecuzione.


0

readonlypuò essere inizializzato alla dichiarazione o ottenere il suo valore solo dal costruttore. Diversamente const, deve essere inizializzato e dichiarato allo stesso tempo. readonly ha tutto const , oltre all'inizializzazione del costruttore

codice 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 ;
        }
    }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.