Storia della domanda:
L'operatore For Each...Next in Visual Basic consente di scorrere gli elementi delle collezioni o degli array. A partire da VB6, è stato supportato lo scorrimento delle collezioni standard, mentre in VB.NET — di tutti i tipi che implementano un'interfaccia specifica. Ma quando è richiesto di creare collezioni personalizzate per la propria iterazione, è importante implementare correttamente l'infrastruttura necessaria.
Problema
Non è sufficiente implementare la memorizzazione degli elementi: gli operatori For Each richiedono il supporto dell'interfaccia specializzata IEnumerable. Spesso, le librerie di collezioni personalizzate si limitavano ai metodi Add/Get, trasformando l'oggetto in un "black box" per l'iterazione. Questo ostacolava l'integrazione con la struttura linguistica di iterazione e portava a errori.
Soluzione
Affinché For Each funzioni correttamente con la tua collezione, è necessario implementare l'interfaccia IEnumerable e, per supportare il lavoro con elementi tipizzati, IEnumerable(Of T). È necessario anche creare un enumeratore (Enumerator) personalizzato che implementi l'interfaccia IEnumerator (o IEnumerator(Of T) nelle versioni generiche).
Esempio:
Public Class IntCollection Implements IEnumerable(Of Integer) Private ReadOnly items As New List(Of Integer)() Public Sub Add(value As Integer) items.Add(value) End Sub Public Function GetEnumerator() As IEnumerator(Of Integer) _ Implements IEnumerable(Of Integer).GetEnumerator Return items.GetEnumerator() End Function Private Function IEnumerable_GetEnumerator() As IEnumerator _ Implements IEnumerable.GetEnumerator Return GetEnumerator() End Function End Class ' Utilizzo: Dim col As New IntCollection() col.Add(10) col.Add(20) col.Add(30) For Each n As Integer In col Console.WriteLine(n) Next
Caratteristiche chiave:
È possibile implementare For Each senza l'interfaccia IEnumerable, semplicemente scrivendo il metodo GetEnumerator?
No, Visual Basic richiede il supporto ufficiale delle interfacce IEnumerable/IEnumerable(Of T) affinché il compilatore riconosca la collezione come compatibile con For Each.
L'enumeratore deve essere una classe separata?
No, se la base dati degli elementi supporta l'enumeratore standard (ad esempio, se aggregi List(Of T)), puoi restituire il suo GetEnumerator. Solo per scenari complessi potrebbe essere necessario una propria implementazione della classe IEnumerator.
È possibile modificare la collezione all'interno del ciclo For Each?
No, modificare la collezione durante l'iterazione porterà a InvalidOperationException. Per una corretta gestione sono necessarie strategie speciali (ad esempio, copiare la lista prima dell'iterazione o utilizzare indicizzazione).
In azienda è stata sviluppata una classe personalizzata MyCollection con aggiunta e rimozione tramite array, ma senza implementare l'interfaccia IEnumerable. For Each non funzionava, dovevamo usare cicli normali e un campo pubblico con gli elementi.
Vantaggi:
Svantaggi:
La classe è stata ristrutturata implementando IEnumerable(Of T). Dopo ciò, la collezione è stata facilmente e standardmente utilizzata in For Each ed è diventata compatibile con LINQ.
Vantaggi:
Svantaggi: