Enumerazione di un insieme
.NET Framework fornisce enumeratori che consentono di scorrere facilmente l'insieme. Gli enumeratori vengono utilizzati solo per leggere i dati in un insieme e non possono essere utilizzati per modificarlo.
Alcuni linguaggi di programmazione forniscono un'istruzione che nasconde la complessità legata all'utilizzo diretto di enumeratori. Nelle istruzioni foreach di C#, for each di C++ e For Each di Visual Basic vengono utilizzati enumeratori.
Informazioni sugli enumeratori
L'enumeratore appiattisce un insieme in modo che si possa accedere ai membri in modo sequenziale. Classi Collection differenti possono avere sequenze differenti. Un enumeratore per un oggetto ArrayList, ad esempio, mantiene l'ordine di inserimento degli elementi nell'insieme, mentre un enumeratore per un oggetto Hashtable visualizza gli elementi in base al relativo codice hash.
Ogni enumeratore si basa sull'interfaccia IEnumerator o sull'interfaccia generica IEnumerator<T>, che richiede i seguenti membri:
La proprietà Current fa riferimento al membro corrente nell'insieme.
La proprietà MoveNext sposta l'enumeratore verso il membro successivo nell'insieme.
La proprietà Reset riporta l'enumeratore all'inizio dell'insieme. Current è posizionato prima del primo elemento. Reset non è disponibile nell'interfaccia IEnumerator<T> generica.
Funzionamento dell'enumeratore
Inizialmente, l'enumeratore è posizionato prima del primo elemento dell'insieme. Reset riporta inoltre l'enumeratore in questa posizione. In questa posizione la proprietà Current non è definita. È pertanto necessario chiamare MoveNext per spostare l'enumeratore in corrispondenza del primo elemento dell'insieme prima di leggere il valore di Current.
Current restituisce lo stesso oggetto fino alla chiamata di MoveNext o Reset. MoveNext imposta Current sull'elemento successivo.
Se MoveNext raggiunge la fine dell'insieme, l'enumeratore viene posizionato dopo l'ultimo elemento dell'insieme e MoveNext restituisce false. Quando l'enumeratore si trova in questa posizione, anche le successive chiamate a MoveNext restituiranno false. Se l'ultima chiamata a MoveNext restituisce false, la proprietà Current non è definita.
Negli insiemi non generici è possibile chiamare Reset seguito da MoveNext per riportare l'enumeratore all'inizio dell'insieme.
Negli insiemi generici non è possibile impostare nuovamente Current sul primo elemento dell'insieme. È necessario creare una nuova istanza di enumeratore.
Un enumeratore rimane valido fino a quando l'insieme rimane invariato. Se vengono apportate modifiche all'insieme, ad esempio l'aggiunta, la modifica o l'eliminazione di elementi, l'enumeratore verrà irrimediabilmente invalidato e il relativo comportamento sarà non definito.
L'enumeratore non dispone di accesso esclusivo all'insieme, pertanto il processo di enumerazione di un insieme non è di per sé thread-safe. Per garantire che l'enumerazione sia thread-safe, è possibile bloccare l'insieme durante l'intera enumerazione. Per consentire l'accesso all'insieme da parte di più thread per la lettura e la scrittura, è necessario implementare una sincronizzazione personalizzata o utilizzare una delle classi di insiemi thread-safe nello spazio dei nomi System.Collections.Concurrent. Le classi System.Collections.Concurrent.ConcurrentQueue<T> e System.Collections.Concurrent.ConcurrentStack<T> creano uno snapshot degli elementi prima di enumerarli, per impedire che l'insieme venga modificato in un altro thread. La classe System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue> non crea uno snapshot.
La classe System.Collections.Concurrent.BlockingCollection<T> fornisce un metodo enumeratore chiamato GetConsumingEnumerable che modifica l'insieme rimuovendo gli elementi dallo stesso mano a mano che vengono enumerati.