逐步解說:在 Visual Basic 中實作 IEnumerable(Of T)

IEnumerable<T> 介面由類別實作,這些類別可以逐個傳回值序列。 逐筆傳回資料的優點是,您不需要將完整的資料集載入記憶體中即可使用。 您只需要使用足夠的記憶體就能從資料載入單一項目。 實作 IEnumerable(T) 介面的類別可以搭配 For Each 迴圈或 LINQ 查詢使用。

例如,請考慮某應用程式必須讀取大型文字檔,並從符合特定搜尋準則的檔案傳回每一行。 應用程式會使用 LINQ 查詢從符合指定準則的檔案傳回行。 若要使用 LINQ 查詢來查詢檔案的內容,應用程式可以將檔案的內容載入陣列或集合。 不過,將整個檔案載入陣列或集合所消耗的記憶體,會遠遠超過需要的。 LINQ 查詢可以改為使用可列舉的類別來查詢檔案內容,並只傳回符合搜尋準則的值。 只傳回少數相符值的查詢所消耗的記憶體少得多。

您可以建立實作 IEnumerable<T> 介面的類別,將來源資料公開為可列舉的資料。 實作 IEnumerable(T) 介面的類別需要另一個實作 IEnumerator<T> 介面的類別,才能逐一查看來源資料。 這兩個類別可讓您以特定類型依序傳回資料項目。

在此逐步解說中,您將建立實作 IEnumerable(Of String) 介面的類別和實作 IEnumerator(Of String) 介面的類別,以逐行讀取文字檔案。

注意

在下列指示的某些 Visual Studio 使用者介面項目中,您的電腦可能會顯示不同的名稱或位置: 您所擁有的 Visual Studio 版本以及使用的設定會決定這些項目。 如需詳細資訊,請參閱將 Visual Studio IDE 個人化

建立可列舉的類別

建立可列舉的類別專案

  1. 在 Visual Basic 的 [檔案] 功能表上,指向 [新增],然後按一下 [專案]

  2. 在 [新增專案] 對話方塊的 [專案類型] 窗格中,確認已選取 [Windows]。 在 [範本] 窗格中,選取 [類別庫]。 在 [名稱] 方塊中,輸入 StreamReaderEnumerable 並按一下 [確定]。 將顯示新專案。

  3. 在 [方案總管] 中,以滑鼠右鍵按一下 Class1.vb 檔案,然後按一下 [重新命名]。 將檔案重新命名為 StreamReaderEnumerable.vb,然後按 ENTER。 重新命名檔案時,也會將類別重新命名為 StreamReaderEnumerable。 此類別會實作 IEnumerable(Of String) 介面。

  4. 以滑鼠右鍵按一下 StreamReaderEnumerable 專案,指向 [新增],然後按一下 [新增項目]。 選取 [類別] 範本。 在 [名稱] 方塊中,鍵入 StreamReaderEnumerator.vb 並按一下 [確定]

此專案中的第一個類別是可列舉的類別,而且會實作 IEnumerable(Of String) 介面。 這個泛型介面會實作 IEnumerable 介面,並保證此類別的取用者可以存取類型為 String 的值。

新增程式碼以實作 IEnumerable

  1. 開啟 StreamReaderEnumerable.vb 檔案。

  2. Public Class StreamReaderEnumerable 之後的行上輸入下列內容,然後按 ENTER 鍵。

    Implements IEnumerable(Of String)
    

    Visual Basic 會自動將 IEnumerable(Of String) 介面所需的成員填入類別。

  3. 這個可列舉的類別會從文字檔逐行讀取各行。 將下列程式碼新增至類別,即可公開以檔案路徑作為輸入參數的公用建構函式。

    Private _filePath As String
    
    Public Sub New(ByVal filePath As String)
        _filePath = filePath
    End Sub
    
  4. 實作 IEnumerable(Of String) 介面的 GetEnumerator 方法會傳回 StreamReaderEnumerator 類別的新執行個體。 實作 IEnumerable 介面的 GetEnumerator 方法可以作為 Private,因為您只能公開 IEnumerable(Of String) 介面的成員。 以下列程式碼取代 Visual Basic 為 GetEnumerator 方法產生的程式碼。

    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
    

新增程式碼以實作 IEnumerator

  1. 開啟 StreamReaderEnumerator.vb 檔案。

  2. Public Class StreamReaderEnumerator 之後的行上輸入下列內容,然後按 ENTER 鍵。

    Implements IEnumerator(Of String)
    

    Visual Basic 會自動將 IEnumerator(Of String) 介面所需的成員填入類別。

  3. 列舉程式類別會開啟文字檔,並執行檔案 I/O 來讀取檔案中的行。 將下列程式碼新增至類別,即可公開以檔案路徑作為輸入參數的公用建構函式,並開啟文字檔以供讀取。

    Private _sr As IO.StreamReader
    
    Public Sub New(ByVal filePath As String)
        _sr = New IO.StreamReader(filePath)
    End Sub
    
  4. IEnumerator(Of String)IEnumerator 介面的 Current 屬性會將文字檔中的目前項目傳回為 String。 實作 IEnumerator 介面的 Current 屬性可以作為 Private,因為您只能公開 IEnumerator(Of String) 介面的成員。 以下列程式碼取代 Visual Basic 為 Current 屬性產生的程式碼。

    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. IEnumerator 介面的 MoveNext 方法會導覽至文字檔中的下一個項目,並更新 Current 屬性所傳回的值。 如果沒有其他項目可讀取,則 MoveNext 方法會傳回 False;否則 MoveNext 方法便會傳回 True。 將下列程式碼新增至 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. IEnumerator 介面的 Reset 方法會引導迭代器指向文字檔的開頭處,並清除目前的項目值。 將下列程式碼新增至 Reset 方法。

    Public Sub Reset() _
        Implements System.Collections.IEnumerator.Reset
    
        _sr.DiscardBufferedData()
        _sr.BaseStream.Seek(0, IO.SeekOrigin.Begin)
        _current = Nothing
    End Sub
    
  7. IEnumerator 介面的 Dispose 方法可確保所有非受控資源都會在迭代器終結之前釋放。 StreamReader 物件所使用的檔案控制代碼為非受控資源,而且必須在迭代器執行個體終結之前關閉。 以下列程式碼取代 Visual Basic 為 Dispose 方法產生的程式碼。

    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
    

使用範例迭代器

您可以在程式碼中將可列舉的類別,與需要實作 IEnumerable 之物件的控制項結構搭配使用,例如 For Next 迴圈或 LINQ 查詢。 下列範例顯示 LINQ 查詢中的 StreamReaderEnumerable

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

Dim results = adminRequests.ToList()

另請參閱