Data: Settembre-2002
Autore: (luKa)
Ambiente: .NET Framework v1.0
Download Esempi
foreach, IEnumerator e IEnumerable
Lo statement foreach permette di iterare tra gli elementi di una collezione.
L'iterazione deve avvenire in sola lettura (se non esplicitamente indicato il contrario). Aggiungere, rimuovere o spostare un elemento durante l'iterazione può causare effetti collaterali.
Si veda:
Microsoft .NET Framework SDK Documentation - C# Programmer's Reference - foreach, in
La collezione deve essere una Interfaccia, una Classe o una Struttura, inoltre deve implementare l'interfaccia IEnumeberable da cui ottenere un iteratore cioè un oggetto che implementa la classe IEnumerator .
A proposito di IEnumerable si veda:
MS .NET Framework SDK Doc - .NET Framework Class Library - IEnumerable Interface
A proposito di IEnumerator si veda:
MS .NET Framework SDK Doc - .NET Framework Class Library - IEnumerator Interface
L'interfaccia ICollection eredita IEnumerable estendendola con metodi per la sincronizzazione e le dimensioni della collezione.
Anche gli array del C# ossia System.Array implementano tali interfaccie e quindi possono essere considerati collezioni.
Si veda l'esempio allegato FOREACH.CS che mostra come accedere alla collezione con il foreach. Lo stesso esempio mostra, a solo scopo dimostrativo, come accedere alla collezione usando direttamente le interfaccie IEnumerable e IEnumerator .Collezioni Strong Typed
L'iteratore accessibile attraverso l'interfaccia IEnumerator è del tutto generico in quanto restituisce l'elemento corrente di tipo Object. E quindi è debolmente tipizzato.
In C# è possibile utilizzare collezioni fortemente tipizzate ossia che implementano una versione "specializzata" dell'interfaccia IEnumerator che restituisce l'elemento corrente del tipo specifico.
Questo ha due vantaggi:
A tale proposito si veda:
- Al momento della compilazione è possibile verificare la corretta corrispondenza tra il tipo trattato dalla collezione e le variabili che sono l'origine o sono destinate ad accogliere gli elementi estratti dalla collezione;
- A Run-Time per le migliori prestazioni che diventano rilevanti qualora gli elementi della collezione siano Value-Type nel qual caso la tipizzazione evita continui Boxing e Unboxing (cioè le conversioni necessarie per poter trattare i Value-Type come oggetti).
MS .NET Framework SDK Doc - C# Programmer's Reference - Collection Classes Tutorial
Un esempio di collezione fortemente tipizzata è la StringCollection che ha un iteratore di tipo StringEnumerator la cui proprietà Current restituisce appunto un oggetto di tipo string ; a tale proposito si veda:
MS .NET Framework SDK Doc - .NET Framework Class Library - StringEnumerator Class
Si veda l'esempio allegato FOREACH_STRONGTYPED.CS che evidenzia la differenza tra collezioni debolmente tipizzate e fortemente tipizzate.
Specificare l'ordinamento
Alcune collezioni prevedono e/o richiedono la disponibilità di un metodo di orinamento per:
- inserire in maniera ordinata gli elementi, verificare eventuali inserimenti doppi, accedere ad un elemento della collezione;
- iterare tra gli elementi di una collezione in maniera ordinata con lo statement foreach;
- ordinare su richiesta una collezione o effettuare una ricerca su di una collezione ordinata.
L'ordinamento, a seconda della collezione usata, può dipendere da vari fattori:
1 - Può dipendere dal valore della chiave Hash (valore numerico ottenuto a partire dalla chiave che determina la posizione dove inserire o cercare l'elemento) restituita dal metodo GetHashCode() dell'elemento della collezione (sia esso l'elemento usato come chiave o valore; questo dipende dalla collezione impiegata); a tale proposito si veda:
MS .NET Framework SDK Doc - .NET Framework Class Lib - Object.GetHashCode Method
le collezioni che ne fanno uso sono: HashTable (per gli elementi usati come Key);
2 - In deroga al metodo GetHashCode() alcune collezioni prevedono la possibilità di indicare una funzione Hash alternativa ottenuta tramite una classe che implementa l'interfaccia IHashCodeProvider ; a tale proposito si veda:
MS .NET Framework SDK Doc - .NET Framework Class Lib - IHashCodeProvider Interface
le collezioni che ne fanno uso sono: HashTable (per gli elementi usati come Key) e NameValueCollection ;
3 - Può dipendere dalla funzione di confronto fornita attraverso l'interfaccia IComparable implementata dall'elemento della collezione (sia esso l'elemento usato come chiave o valore; questo dipende dalla collezione impiegata); a tale proposito si veda:
MS .NET Framework SDK Doc - .NET Framework Class Lib - IComparable Interface
le collezioni che ne fanno uso sono: System.Array (per i metodi BinarySearch e Sort), ArrayList (per i metodi BinarySearch e Sort) e SortedList ;
4 - In deroga alla funzione di confronto fornita attraverso l'interfaccia IComparable alcune collezioni prevedono la possibilità di indicare criteri di confronto alternativi tramite una classe che implementa l'interfaccia IComparer; a tale proposito si veda:
MS .NET Framework SDK Doc - .NET Framework Class Lib - IComparer Interface
le collezioni che ne fanno uso sono: System.Array (per i metodi BinarySearch e Sort), Array List (per i metodi BinarySearch e Sort), HashTable (per gli elementi usati come Key), SortedList , ListDictionary e NameValueCollection;
La chiave Hash restituita (tramite la funzione GetHashCode ) ed il criterio di confronto (effettuato tramite IComparable ) devono essere definiti coerentemente col criterio di confronto implementato facendo l'overrite del metodo Equals e l'overloading dell' operatore di ugualianza ==.
A tale proposito si veda:
MS .NET Framework SDK Doc - .NET Framework General Reference - Guidelines for Implementing Equals and the Equality Operator (==)
Si veda l'esempio HASH.CS che mostra l'implementazione e l'utilizzo del metodo GetHashCode() .
Si vedano i seguenti esempi:
- SORT0.CS che evidenzia la necessità dell'interfaccia IComparable per l'ordinamento
- SORT1.CS che implementa IComparable con ordinamento alfabetico (Cognome, Nome)
- SORT2.CS che implementa il metodo Equals coerentemente con IComparable
- SORT3.CS che implementa il metodo GetHashCode() coerentemente con Equals
- SORT4.CS che implementa l'operatore di uguaglianza == e disugualianza != coerentemente con Equals
- SORT5.CS che implementa tramite IComparer un criterio di ordinamento alternativo
foreach e l'ordine di accesso
Il foreach accede agli elementi di una collezione attraverso un IEnumerator.
L'ordine con cui un IEnumerator fornisce gli elementi è casuale (è determinato da dettagli implementativi che possono variare senza preavviso) se non diversamente dichiarato esplicitamente nella documentazione di una collezione.
Per le collezioni di tipo (Chiave, Valore) ossia IDictionary è necessario esista un ordinamento per le chiavi e quindi è possibile che IEnumerator restituisca gli elementi secondo tale ordine (verificare nella documentazione della singola collezione).
Per le collezioni di valori ossia IList eventuali ordinamenti disponibili sugli elementi possono essere utilizzati da metodi quali ad esempio BinarySearch e Sort, ma questi non hanno effetto diretto sull'ordine con cui foreach accede agli elementi.
Per le collezioni quali System.Array (o che sono basate sull'Array come ArrayList) con quale ordine il foreach accede agli elementi dell'array? Il criterio è il seguente:
- per gli Array a singola dimensione gli elementi vengono acceduti paretendo dall'indice 0 sino all'indice Length -1;
- per gli Array a più dimensione gli elementi vengono acceduti incrementando progressivamente gli indici partendo da quelli più a destra.
Se si desidera un comportamento differente è sempre possibile implementare degli IEnumerator specializzati, come nell'esempio allegato (di Eric Gunnerson in persona!) iter.zip .