Domanda

Ho un oggetto in un ambiente multi-thread che mantiene una raccolta di informazioni, ad esempio:

public IList<string> Data 
{
    get 
    {
        return data;
    }
}

Attualmente ho return data; avvolto da a ReaderWriterLockSlim per proteggere la raccolta da violazioni di condivisione.Tuttavia, per essere doppiamente sicuro, vorrei restituire la raccolta come di sola lettura, in modo che il codice chiamante non sia in grado di apportare modifiche alla raccolta, ma solo visualizzare ciò che è già presente.E' possibile?

È stato utile?

Soluzione

Se il tuo unico intento è ottenere il codice chiamante per non commettere errori e modificare la raccolta quando dovrebbe essere solo in lettura, tutto ciò che è necessario è restituire un'interfaccia che non supporta Aggiungi, Rimuovi, ecc.Perché non tornare IEnumerable<string>?Il codice chiamante dovrebbe essere lanciato, cosa che difficilmente potrebbero fare senza conoscere i dettagli interni della proprietà a cui stanno accedendo.

Se però il tuo intento è quello di impedire al codice chiamante di osservare gli aggiornamenti provenienti da altri thread dovrai ricorrere alle soluzioni già citate, per eseguire una copia profonda o superficiale a seconda delle tue necessità.

Altri suggerimenti

Se i tuoi dati sottostanti sono archiviati come elenco puoi utilizzare Elenco(T).AsReadOnly metodo.
Se i tuoi dati possono essere enumerati, puoi utilizzare Enumerabile.ToList per trasmettere la tua raccolta a List e chiamare AsReadOnly su di essa.

Ho votato per la tua risposta accettata e sono d'accordo, tuttavia potrei darti qualcosa da considerare?

Non restituire direttamente una collezione.Crea una classe di logica aziendale dal nome accurato che rifletta lo scopo della raccolta.

Il vantaggio principale di ciò sta nel fatto che non è possibile aggiungere codice alle raccolte, quindi ogni volta che hai una "raccolta" nativa nel tuo modello a oggetti, hai SEMPRE codice di supporto non OO diffuso nel tuo progetto per accedervi.

Ad esempio, se la tua raccolta fosse costituita da fatture, probabilmente avresti 3 o 4 punti nel tuo codice in cui hai ripetuto le fatture non pagate.Potresti avere un metodo getUnpaidInvoices.Tuttavia, il vero potere arriva quando inizi a pensare a metodi come "payUnpaidInvoices(pagatore, conto);".

Quando passi in giro raccolte invece di scrivere un modello a oggetti, non ti verranno mai in mente intere classi di refactoring.

Nota anche che questo rende il tuo problema particolarmente piacevole.Se non vuoi che le persone modifichino le raccolte, il tuo contenitore non deve contenere mutatori.Se in seguito decidi che in un solo caso DEVI effettivamente modificarlo, puoi creare un meccanismo sicuro per farlo.

Come risolvi questo problema quando distribuisci una collezione nativa?

Inoltre, le raccolte native non possono essere migliorate con dati aggiuntivi.Lo riconoscerai la prossima volta che scopri di passare (Raccolta, Extra) a più di uno o due metodi.Indica che "Extra" appartiene all'oggetto contenente la tua collezione.

Penso che tu stia confondendo i concetti qui.

IL ReadOnlyCollection fornisce un wrapper di sola lettura per una raccolta esistente, consentendo all'utente (Classe A) di trasmettere un riferimento alla raccolta con la consapevolezza che il chiamante (Classe B) non può modificare la raccolta (ovveronon può aggiungere O rimuovere qualsiasi elemento della collezione.)

Non ci sono assolutamente garanzie di sicurezza del thread.

  • Se tu (Classe A) continui a modificare la raccolta sottostante dopo averla distribuita come a ReadOnlyCollection quindi la classe B vedrà queste modifiche, tutti gli iteratori verranno invalidati, ecc.e generalmente essere aperti a qualsiasi dei soliti problemi di concorrenza con le raccolte.
  • Inoltre, se gli elementi all'interno della raccolta sono mutabili, entrambi (Classe A) E il chiamante (Classe B) sarà in grado di modificare qualsiasi stato mutabile degli oggetti all'interno della raccolta.

L'implementazione dipende dalle tue esigenze:- Se non ti interessa che il chiamante (Classe B) veda eventuali ulteriori modifiche alla raccolta, puoi semplicemente clonare la raccolta, distribuirla e smettere di preoccuparti.- Se hai assolutamente bisogno che il chiamante (Classe B) veda le modifiche apportate alla raccolta e vuoi che questo sia thread-safe, allora hai più di un problema tra le mani.Una possibilità è implementare la propria variante thread-safe di ReadOnlyCollection per consentire l'accesso bloccato, anche se questo non sarà banale e non performante se si desidera supportare IEnumerable e Ancora non ti proteggerà dagli elementi mutabili nella raccolta.

Bisogna tenerlo presente akuLa risposta di proteggerà l'elenco solo come di sola lettura.Gli elementi nell'elenco sono ancora molto scrivibili.Non so se esiste un modo per proteggere gli elementi non atomici senza clonarli prima di inserirli nell'elenco di sola lettura.

Vuoi usare il prodotto parola chiave.Si scorre l'elenco IEnumerable e si restituiscono i risultati con yeild.Ciò consente al consumatore di utilizzare for ciascuno senza modificare la raccolta.

Sarebbe qualcosa del genere:

List<string> _Data;
public IEnumerable<string> Data
{
  get
  {
    foreach(string item in _Data)
    {
      return yield item;
    }
  }
}

Puoi invece utilizzare una copia della raccolta.

public IList<string> Data {
get {
    return new List<T>(data);
}}

In questo modo non importa se viene aggiornato.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top