Procedura dettagliata: implementazione di IEnumerable(Of T) in Visual Basic

L'interfaccia IEnumerable<T> viene implementata dalle classi che possono restituire una sequenza di valori un elemento alla volta. Il vantaggio di restituire dati un elemento alla volta è che non è necessario caricare il set completo di dati in memoria per usarlo. Occorre solo usare memoria sufficiente per caricare un singolo elemento dai dati. Le classi che implementano l'interfaccia IEnumerable(T) possono essere usate con cicli For Each o query LINQ.

Si consideri, ad esempio, un'applicazione che deve leggere un file di testo di grandi dimensioni e restituire ogni riga dal file che corrisponde a criteri di ricerca specifici. L'applicazione usa una query LINQ per restituire righe dal file che soddisfano i criteri specificati. Per eseguire query sul contenuto del file usando una query LINQ, l'applicazione potrebbe caricare il contenuto del file in una matrice o in una raccolta. Il caricamento dell'intero file in una matrice o in una raccolta utilizzerebbe tuttavia molto più memoria di quanto richiesto. La query LINQ potrebbe invece eseguire query sul contenuto del file usando una classe enumerabile, restituendo solo valori che corrispondono ai criteri di ricerca. Le query che restituiscono solo alcuni valori corrispondenti consumano una quantità decisamente inferiore di memoria.

È possibile creare una classe che implementa l'interfaccia IEnumerable<T> per esporre i dati di origine come dati enumerabili. La classe che implementa l'interfaccia IEnumerable(T) richiederà un'altra classe che implementa l'interfaccia IEnumerator<T> per eseguire l'iterazione dei dati di origine. Queste due classi consentono di restituire elementi di dati in sequenza come tipo specifico.

In questa procedura dettagliata verrà creata una classe che implementa l'interfaccia IEnumerable(Of String) e una classe che implementa l'interfaccia IEnumerator(Of String) per leggere un file di testo una riga alla volta.

Nota

I nomi o i percorsi visualizzati per alcuni elementi dell'interfaccia utente di Visual Studio nelle istruzioni seguenti potrebbero essere diversi nel computer in uso. La versione di Visual Studio in uso e le impostazioni configurate determinano questi elementi. Per altre informazioni, vedere Personalizzazione dell'IDE.

Creazione della classe Enumerable

Creare il progetto di classe enumerabile

  1. In Visual Basic scegliere Nuovo dal menu File e quindi fare clic su Progetto.

  2. Nel riquadro Tipi di progetto della finestra di dialogo Nuovo progetto verificare che sia selezionata l'opzione Windows. Selezionare Libreria di classi nel riquadro Modelli. Nella casella Nome digitare StreamReaderEnumerable e quindi fare clic su OK. Viene visualizzato il nuovo progetto.

  3. In Esplora soluzioni fare clic con il pulsante destro del mouse sul file Class1.vb e fare clic su Rinomina. Rinominare il file StreamReaderEnumerable.vb e premere INVIO. Modificando il nome del file, anche la classe verrà rinominata StreamReaderEnumerable. Questa classe implementerà l'interfaccia IEnumerable(Of String).

  4. Fare clic con il pulsante destro del mouse sul progetto StreamReaderEnumerable, scegliere Aggiungi e quindi fare clic su Nuovo elemento. Selezionare il modello Classe. Digitare StreamReaderEnumerator.vb nella casella Nome e quindi fare clic su OK.

La prima classe in questo progetto è la classe enumerabile e implementerà l'interfaccia IEnumerable(Of String). Questa interfaccia generica implementa l'interfaccia IEnumerable e garantisce che i consumer di questa classe possano accedere ai valori tipizzati come String.

Aggiungere il codice per implementare IEnumerable

  1. Aprire il file StreamReaderEnumerable.vb.

  2. Nella riga dopo Public Class StreamReaderEnumerable digitare quanto segue e premere INVIO.

    Implements IEnumerable(Of String)
    

    Visual Basic popola automaticamente la classe con i membri richiesti dall'interfaccia IEnumerable(Of String).

  3. Questa classe enumerabile leggerà le righe da un file di testo una riga alla volta. Aggiungere il codice seguente alla classe per esporre un costruttore pubblico che accetta un percorso di file come parametro di input.

    Private _filePath As String
    
    Public Sub New(ByVal filePath As String)
        _filePath = filePath
    End Sub
    
  4. L'implementazione del metodo GetEnumerator dell'interfaccia IEnumerable(Of String) restituirà una nuova istanza della classe StreamReaderEnumerator. L'implementazione del metodo GetEnumerator dell'interfaccia IEnumerable può essere resa Private, perché è necessario esporre solo i membri dell'interfaccia IEnumerable(Of String). Sostituire il codice generato da Visual Basic per i metodi GetEnumerator con il codice seguente.

    Public Function GetEnumerator() As IEnumerator(Of String) _
        Implements IEnumerable(Of String).GetEnumerator
    
        Return New StreamReaderEnumerator(_filePath)
    End Function
    
    Private Function GetEnumerator1() As IEnumerator _
        Implements IEnumerable.GetEnumerator
    
        Return Me.GetEnumerator()
    End Function
    

Aggiungere il codice per implementare IEnumerator

  1. Aprire il file StreamReaderEnumerator.vb.

  2. Nella riga dopo Public Class StreamReaderEnumerator digitare quanto segue e premere INVIO.

    Implements IEnumerator(Of String)
    

    Visual Basic popola automaticamente la classe con i membri richiesti dall'interfaccia IEnumerator(Of String).

  3. La classe enumeratore apre il file di testo ed esegue l'I/O del file per leggere le righe dal file. Aggiungere il codice seguente alla classe per esporre un costruttore pubblico che accetta un percorso di file come parametro di input e aprire il file di testo per la lettura.

    Private _sr As IO.StreamReader
    
    Public Sub New(ByVal filePath As String)
        _sr = New IO.StreamReader(filePath)
    End Sub
    
  4. Le proprietà Current per le interfacce IEnumerator(Of String) e IEnumerator restituiscono l'elemento corrente dal file di testo come String. L'implementazione della proprietà Current dell'interfaccia IEnumerator può essere resa Private, perché è necessario esporre solo i membri dell'interfaccia IEnumerator(Of String). Sostituire il codice generato da Visual Basic per le proprietà Current con il codice seguente.

    Private _current As String
    
    Public ReadOnly Property Current() As String _
        Implements IEnumerator(Of String).Current
    
        Get
            If _sr Is Nothing OrElse _current Is Nothing Then
                Throw New InvalidOperationException()
            End If
    
            Return _current
        End Get
    End Property
    
    Private ReadOnly Property Current1() As Object _
        Implements IEnumerator.Current
    
        Get
            Return Me.Current
        End Get
    End Property
    
  5. Il metodo MoveNext dell'interfaccia IEnumerator passa all'elemento successivo nel file di testo e aggiorna il valore restituito dalla proprietà Current. Se non sono presenti altri elementi da leggere, il metodo MoveNext restituisce False. In caso contrario, il metodo MoveNext restituisce True. Aggiungere il codice seguente al metodo MoveNext.

    Public Function MoveNext() As Boolean _
        Implements System.Collections.IEnumerator.MoveNext
    
        _current = _sr.ReadLine()
        If _current Is Nothing Then Return False
        Return True
    End Function
    
  6. Il metodo Reset dell'interfaccia IEnumerator indirizza l'iteratore in modo che punti all'inizio del file di testo e cancella il valore dell'elemento corrente. Aggiungere il codice seguente al metodo Reset.

    Public Sub Reset() _
        Implements System.Collections.IEnumerator.Reset
    
        _sr.DiscardBufferedData()
        _sr.BaseStream.Seek(0, IO.SeekOrigin.Begin)
        _current = Nothing
    End Sub
    
  7. Il metodo Dispose dell'interfaccia IEnumerator garantisce che tutte le risorse non gestite vengano rilasciate prima che l'iteratore venga eliminato definitivamente. L'handle di file usato dall'oggetto StreamReader è una risorsa non gestita e deve essere chiuso prima che l'istanza dell'iteratore venga eliminata definitivamente. Sostituire il codice generato da Visual Basic per il metodo Dispose con il codice seguente.

    Private disposedValue As Boolean = False
    
    Protected Overridable Sub Dispose(ByVal disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                ' Dispose of managed resources.
            End If
            _current = Nothing
            _sr.Close()
            _sr.Dispose()
        End If
    
        Me.disposedValue = True
    End Sub
    
    Public Sub Dispose() Implements IDisposable.Dispose
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
    
    Protected Overrides Sub Finalize()
        Dispose(False)
    End Sub
    

Uso dell'iteratore di esempio

È possibile usare una classe enumerabile nel codice insieme alle strutture di controllo che richiedono un oggetto che implementa IEnumerable, ad esempio un ciclo For Next o una query LINQ. Nell'esempio seguente viene illustrato StreamReaderEnumerable in una query LINQ.

Dim adminRequests =
    From line In New StreamReaderEnumerable("..\..\log.txt")
    Where line.Contains("admin.aspx 401")

Dim results = adminRequests.ToList()

Vedi anche