Domanda

Ho un ICollection<T> chiamato foos nella mia classe che voglio esporre in sola lettura (vedi questa domanda ). Vedo che l'interfaccia definisce una proprietà .IsReadOnly, che sembra appropriata ... La mia domanda è questa: come posso rendere ovvio al consumatore della classe che .Add() è di sola lettura?

Non voglio fare affidamento sul fatto che si siano ricordati di interrogare ReadOnlyCollection<T> prima di provare un metodo non implementato come IList<T>. Idealmente, vorrei esporre foo come GetReadOnlyFooCollection, ma non implementa ToList(). Devo esporre <=> tramite un metodo chiamato, ad esempio, <=> anziché tramite una proprietà? In tal caso, ciò non confonderebbe qualcuno che si aspetta quindi un <=>?

Questo è C # 2.0, quindi i metodi di estensione come <=> non sono disponibili ...

È stato utile?

Soluzione

Sembra che io abbia deciso di restituire IEnumerable con gli oggetti clonati.

public IEnumerable<Foose> GetFooseList() {
   foreach(var foos in Collection) {
     yield return foos.Clone();
   }
}
  • richiede un metodo Clone su Foos.

Ciò non consente modifiche nella raccolta. Ricorda che ReadonlyCollection è & Quot; leaky & Quot; poiché gli oggetti al suo interno possono essere modificati come indicato in un link in un altro post.

Altri suggerimenti

Puoi fare " foos " una raccolta ReadOnly come questa:

ReadOnlyCollection<T> readOnlyCollection = foos.ToList<T>().AsReadOnly();

Quindi puoi esporlo come proprietà della tua classe.

EDIT:

    class FooContainer
    {
        private ICollection<Foo> foos;
        public ReadOnlyCollection<Foo> ReadOnlyFoos { get { return foos.ToList<Foo>().AsReadOnly();} }

    }

Nota: una volta ottenuta la raccolta ReadOnlyFoos non è più " sincronizzata " con i tuoi amici ICollection. Vedi la cui hai fatto riferimento .

Da quando la domanda è stata scritta, .NET 4.0 ha aggiunto un'interfaccia IReadOnlyCollection<T>; sarebbe probabilmente utile usarlo come tipo di ritorno dichiarato.

Ciò, tuttavia, lascia aperta la questione del tipo di istanza da restituire. Un approccio sarebbe quello di clonare tutti gli elementi della collezione originale. Un altro sarebbe restituire sempre un wrapper di sola lettura. Un terzo sarebbe restituire la raccolta originale se implementa List<T>. Ogni approccio sarà il migliore in alcuni contesti, ma sarà meno che ideale (o forse addirittura terribile) in altri. Sfortunatamente, Microsoft non fornisce mezzi standard con i quali porre una domanda a due domande molto importanti:

  1. Prometti di contenere sempre e per sempre gli stessi articoli che fai adesso?

  2. Puoi essere esposto in modo sicuro direttamente al codice che non dovrebbe modificare i tuoi contenuti.

Quale stile di wrapping è appropriato dipende da ciò che il codice client si aspetta di fare con ciò che riceve. Alcuni scenari da evitare:

  1. È stato fornito un oggetto di un tipo che il client riconoscerebbe come immutabile, ma anziché essere restituito direttamente viene duplicato, utilizzando un tipo che il client non riconosce come immutabile. Di conseguenza, il cliente è costretto a duplicare nuovamente la raccolta.

  2. Un oggetto è stato fornito di un tipo che il client riconoscerebbe come immutabile, ma prima di essere restituito è avvolto in modo tale che il client non può dire se la collezione è immutabile o meno, e quindi è costretto a duplicare.

  3. Un oggetto di tipo mutabile che non dovrebbe essere mutato viene fornito da un client (trasmesso a un'interfaccia di sola lettura). Viene quindi esposto direttamente a un altro client che determina che si tratta di un tipo mutabile e procede a modificarlo.

  4. Viene ricevuto un riferimento a una raccolta mutabile che viene incapsulato in un wrapper di sola lettura prima di essere restituito a un client che necessita di un oggetto immutabile. Il metodo che ha restituito la raccolta ha promesso che è immutabile, e quindi il cliente ha rifiutato di fare la propria copia difensiva. Il cliente è quindi mal preparato per la possibilità che la raccolta possa cambiare.

Non c'è davvero alcuna particolare " safe " corso di azione che un oggetto può eseguire con le raccolte che riceve da alcuni client e che deve esporre ad altri. La duplicazione sempre di tutto è in molte circostanze il modo più sicuro di agire, ma può facilmente provocare situazioni in cui una raccolta che non dovrebbe essere duplicata finisce per essere duplicata centinaia o migliaia di volte. Restituire i riferimenti ricevuti può spesso essere l'approccio più efficiente, ma può anche essere semanticamente pericoloso.

Vorrei che Microsoft aggiungesse un metodo standard con cui le raccolte potevano essere poste alle domande precedenti o, meglio ancora, essere invitate a produrre un'istantanea immutabile del loro stato attuale. Una raccolta immutabile potrebbe restituire un'istantanea immutabile del suo stato attuale in modo molto economico (basta restituire se stessa) e anche alcuni tipi di raccolta mutabili potrebbero restituire un'istantanea immutabile a un costo molto inferiore al costo di un elenco completo (ad esempio un T[][] potrebbe essere supportato da due ICloneable matrici, una delle quali contiene riferimenti a matrici immutabili condivisibili di 256 elementi e l'altra contiene riferimenti a matrici mutevoli non condivisibili di 256 elementi. La creazione di un'istantanea di un elenco richiederebbe la clonazione solo delle matrici interne contenenti elementi che sono stati modificati dall'ultima istantanea, potenzialmente molto più economica della clonazione dell'intera lista. Sfortunatamente, poiché non esiste un " standard; crea un'istantanea immutabile " interfaccia [nota che <=> non conta , poiché un clone di un elenco mutabile sarebbe mutabile, mentre si potrebbe fare un'istantanea immutabile incapsulando un clone mutabilein un wrapper di sola lettura, che funzionerebbe solo per cose che sono clonabili, e anche i tipi che non sono clonabili dovrebbero comunque supportare un " snapshot mutable " la funzione.]

La mia raccomandazione è di tornare usando un ReadOnlyCollection < T > per lo scenario direttamente. Questo rende l'uso esplicito per l'utente chiamante.

Normalmente suggerirei di utilizzare l'interfaccia appropriata. Ma dato che .NET Framework attualmente non ha un IReadOnlyCollection adatto, è necessario utilizzare il tipo ReadOnlyCollection.

Inoltre devi essere consapevole quando usi ReadOnlyCollection, perché in realtà non è di sola lettura: Immutabilità e ReadOnlyCollection

A volte potresti voler usare un'interfaccia, forse perché vuoi deridere la raccolta durante i test delle unità. Si prega di consultare il mio voce di blog per aggiungere la propria interfaccia a ReadonlyCollection utilizzando un adattatore.

Di solito restituisco un IEnumerable < T > .

Una volta che hai creato una raccolta di sola lettura (quindi metodi come Aggiungi , Rimuovi e Clear non funzionano più) non è rimasto molto che una collezione sostiene che un enumerabile no - solo Count e contiene , credo.

Se i consumatori della tua classe hanno davvero bisogno di trattare elementi come se fossero in una raccolta, è abbastanza facile passare un IEnumerable a Elenco < T > .

Restituisce una T []:

private ICollection<T> items;

public T[] Items
{
    get { return new List<T>(items).ToArray(); }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top