VB.NET: il ciclo For Each e le interfacce IEnumerable e IEnumerator

La prima cosa che mi ha colpito della versione 2.0 del framework, ed in particolare del nuovo VB, è stata la possibilità di creare classi Generic, il cui tipo cioè venga determinato solo al momento dell’uso, pur mantenendo i vantaggi della tipizzazione forte. L’uso che viene spontaneo è quello delle collection, sulle quali possiamo definire le nostre proprietà e metodi di estensione. Fatto ciò volevo fare in modo che la mia collection potesse essere scorsa con un bel loop For Each; documentazione alla mano si tratta di implementare l’interfaccia IEnumerable e definire l’unico metodo GetEnumerator. Semplice, no? Quasi, perchè a questo punto il diavolo ci ha messo la coda…

Innanzitutto, la documentazione che stavo leggendo si riferiva all’implementazione dell’interfaccia IEnumerable normale, non Generic. Al che le mie uniche due sinapsi ripresesi dai bagordi festivi hanno cominciato una subdola opera di persuasione per convincermi che io dovevo assolutamente implementare l’interfaccia Generic.IEnumerable.

In secondo luogo, l’interfaccia IEnumerable da sola, non basta mica! Pretende a tutti i costi di essere accompagnata dalla sua cara sorella, IEnumerator. Anche in questo caso le due sinapsi si sono messe di buzzo buono per confondermi le idee, con la versione Generics.IEnumerator.

Credo che la cosa più semplice ed immediata sia presentare il codice (molto scheletrico, beninteso) e commentarlo:

Public Class GenericCollection(Of T)
  ' La classe Generic per la quale realizzare la scansione tramite For Each...
  ' A scanso di equivoci, qui non presento i metodi aggiutivi rispetto
  ' alla Generics.ArrayList standard

  Implements IEnumerable
  ' Prima interfaccia da implementare: espone il metodo GetEnumerator

  Implements IEnumerator
  ' Seconda interfaccia da implementare: espone i metodi Reset e MoveNext
  ' e la proprietà Current

  Private m_Position As Integer = -1
  ' La "posizione" del cursore all'interno della collection.
  ' N.B.: DEVE essere inizializzata al valore -1: se non venisse inizializzata
  ' assumerebbe il valore di default 0 (ZERO) che provocherebbe un malfunzionamento

  Private m_Values As New ArrayList
  ' L'arrayList dei valori

  Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
  ' Non c'è molto da dire, se non che deve essere implementata così com'è scritta....
   Return CType(Me, IEnumerator)

  End Function

  Public Function MoveNext() As Boolean Implements IEnumerator.MoveNext
  ' Su questa è meglio spendere due paroline: la MoveNext viene richiamata
  ' all'inizializzazione dell'enumeratore e la prima cosa che fa è incrementare
  ' la posizione del cursore nella Collection (o ArrayList, in questo caso).
  ' Questo spiega perchè è così importante inizializzare correttamente
  ' la variabile m_Position: lasciando il default = 0 la prima chiamata
  ' al metodo MoveNext imposterebbe la variabile a 1: l'effetto finale
  ' sarebbe che il ciclo For Each "salterebbe" il primo elemento (avente
  ' indice = 0)!

   m_Position += 1
   Return (m_Position > m_ValueTable.Count)

  End Function

  Public Sub Reset() Implements IEnumerator.Reset
   m_Position = -1

  End Sub

  Public ReadOnly Property Current() As Object Implements IEnumerator.Current
   Get
  ' In questo caso è sufficente un cast dell'elemento nella collection
  ' al tipo T
    Return CType(m_Values.Item(m_Position), T)

   End Get

  End Property

End Class

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

*