Come creo una classe immutabile?


113

Sto lavorando alla creazione di una classe immutabile.
Ho contrassegnato tutte le proprietà come di sola lettura.

Ho un elenco di elementi nella classe.
Sebbene se la proprietà è di sola lettura, l'elenco può essere modificato.

L'esposizione dell'IEnumerable dell'elenco lo rende immutabile.
Volevo sapere quali sono le regole di base da seguire per rendere immutabile una classe?


2
Consiglio vivamente di leggere la serie del blog di Eric Lippert sull'immutabilità , in particolare la voce sui "tipi di immutabilità" . Il tuo commento secondo cui "Esponendo l'IEnumerable dell'elenco lo rende immutabile" mi sembra un po 'strano. Cosa intendi dire?
Jon Skeet

Quello che intendevo era, piuttosto che consentire l'accesso all'elenco (se l'oggetto ha un elenco di alcuni altri oggetti), consentire all'utente di accedere ai membri con IEnumerable. Elenco di conversazione qui come in un esempio specifico, ma può essere qualsiasi struttura di dati.
Biswanath

1
I collegamenti di Jon Skeet alla serie di blog di Eric Lippert si sono interrotti quando MSDN ha archiviato questi blog. Vedere "tipi di immutabilità" . Il resto della serie sembra essere sotto dicembre 2007 + gennaio 2008 nel riquadro di sinistra.
John Doe il

Si prega di vedere la serie di blog di Eric Lippert su come le persone comunemente confondono i termini atomicity, volatility, e immutability: Parte prima , Parte Due , e Part Three . Questi provengono dal suo blog personale e, credo, più adatti ai principianti dei suoi post su MSDN.
John Doe il

Risposte:


121

Penso che tu sia sulla strada giusta -

  • tutte le informazioni inserite nella classe dovrebbero essere fornite nel costruttore
  • tutte le proprietà dovrebbero essere solo getter
  • se una collezione (o Array) viene passata al costruttore, dovrebbe essere copiata per impedire al chiamante di modificarla in seguito
  • se hai intenzione di restituire la tua raccolta, restituisci una copia o una versione di sola lettura (ad esempio, utilizzando ArrayList.ReadOnly o simili: puoi combinarlo con il punto precedente e memorizzare una copia di sola lettura da restituire quando i chiamanti vi accedono), restituiscono un enumeratore o utilizzano un altro metodo / proprietà che consente l'accesso in sola lettura alla raccolta
  • tieni presente che potresti ancora avere l'aspetto di una classe mutabile se uno dei tuoi membri è mutabile - in tal caso, dovresti copiare lo stato che desideri mantenere ed evitare di restituire interi oggetti mutabili, a meno che non li copi prima di restituirli al chiamante - un'altra opzione è quella di restituire solo "sezioni" immutabili dell'oggetto modificabile - grazie a @Brian Rasmussen per avermi incoraggiato ad espandere questo punto

Devo scrivere una proprietà di ricerca wrapper, che ti consenta di eseguire solo la ricerca? E grazie per la risposta.
Biswanath

5
Qualsiasi tipo di riferimento modificabile passato come argomento al costruttore dovrebbe essere copiato. In caso contrario, il chiamante manterrà ancora un riferimento allo stato.
Brian Rasmussen

@Brian Rasmussen, hai ragione, ma anche se viene copiato, qualsiasi chiamante potrebbe accedere all'oggetto modificabile, a seconda degli accessi forniti. Nel caso in cui venga passato un oggetto modificabile, la classe è meglio che restituisca sempre una copia diversa, o sezioni immutabili dell'oggetto
Blair Conrad

@ Blair - Se ho un dizionario nella classe che voglio solo usarlo per la ricerca. Una proprietà di sola lettura che controlla dovrebbe andare bene?
Biswanath

@Biswanath - sì, con l'avvertenza che se i valori nel dizionario sono mutabili, dovrai proteggerli prima di tornare, se non vuoi che vengano mutati
Blair Conrad

18

Per essere immutabili, tutte le proprietà e i campi devono essere di sola lettura. E gli elementi in qualsiasi elenco dovrebbero essere essi stessi immutabili.

È possibile creare una proprietà elenco di sola lettura come segue:

public class MyClass
{
    public MyClass(..., IList<MyType> items)
    {
        ...
        _myReadOnlyList = new List<MyType>(items).AsReadOnly();
    }

    public IList<MyType> MyReadOnlyList
    {
        get { return _myReadOnlyList; }
    }
    private IList<MyType> _myReadOnlyList

}

1
Penso che ImmutableList sarebbe migliore.
Kevin Wong

usa ImmutableList, considera anche di sigillare la classe
kofifus

Io non sono convinto che ImmutableList è una buona misura per questo caso d'uso: basic rules to make a class (that contains a list) immutable. La semantica ImmutableList consente un utilizzo più efficiente della memoria quando si aggiungono elementi a una copia dell'elenco, ma mi sembra che sia un caso d'uso più specifico.
Joe

11

Inoltre, tieni presente che:

public readonly object[] MyObjects;

non è immutabile anche se è contrassegnato con la parola chiave di sola lettura. È comunque possibile modificare singoli riferimenti / valori di array tramite la funzione di accesso dell'indice.


5

Usa la ReadOnlyCollectionclasse. Si trova nello System.Collections.ObjectModelspazio dei nomi.

Su tutto ciò che restituisce l'elenco (o nel costruttore), imposta l'elenco come raccolta di sola lettura.

using System.Collections.ObjectModel;

...

public MyClass(..., List<ListItemType> theList, ...)
{
    ...
    this.myListItemCollection= theList.AsReadOnly();
    ...
}

public ReadOnlyCollection<ListItemType> ListItems
{
     get { return this.myListItemCollection; }
}

1

Un'altra opzione sarebbe quella di utilizzare un pattern visitatore invece di esporre del tutto le raccolte interne.


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.