逐步解說:在 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 個人化。
建立可列舉的類別
建立可列舉的類別專案
在 Visual Basic 的 [檔案] 功能表上,指向 [新增],然後按一下 [專案]。
在 [新增專案] 對話方塊的 [專案類型] 窗格中,確認已選取 [Windows]。 在 [範本] 窗格中,選取 [類別庫]。 在 [名稱] 方塊中,輸入
StreamReaderEnumerable
並按一下 [確定]。 將顯示新專案。在 [方案總管] 中,以滑鼠右鍵按一下 Class1.vb 檔案,然後按一下 [重新命名]。 將檔案重新命名為
StreamReaderEnumerable.vb
,然後按 ENTER。 重新命名檔案時,也會將類別重新命名為StreamReaderEnumerable
。 此類別會實作IEnumerable(Of String)
介面。以滑鼠右鍵按一下 StreamReaderEnumerable 專案,指向 [新增],然後按一下 [新增項目]。 選取 [類別] 範本。 在 [名稱] 方塊中,鍵入
StreamReaderEnumerator.vb
並按一下 [確定]。
此專案中的第一個類別是可列舉的類別,而且會實作 IEnumerable(Of String)
介面。 這個泛型介面會實作 IEnumerable 介面,並保證此類別的取用者可以存取類型為 String
的值。
新增程式碼以實作 IEnumerable
開啟 StreamReaderEnumerable.vb 檔案。
在
Public Class StreamReaderEnumerable
之後的行上輸入下列內容,然後按 ENTER 鍵。Implements IEnumerable(Of String)
Visual Basic 會自動將
IEnumerable(Of String)
介面所需的成員填入類別。這個可列舉的類別會從文字檔逐行讀取各行。 將下列程式碼新增至類別,即可公開以檔案路徑作為輸入參數的公用建構函式。
Private _filePath As String Public Sub New(ByVal filePath As String) _filePath = filePath End Sub
實作
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
開啟 StreamReaderEnumerator.vb 檔案。
在
Public Class StreamReaderEnumerator
之後的行上輸入下列內容,然後按 ENTER 鍵。Implements IEnumerator(Of String)
Visual Basic 會自動將
IEnumerator(Of String)
介面所需的成員填入類別。列舉程式類別會開啟文字檔,並執行檔案 I/O 來讀取檔案中的行。 將下列程式碼新增至類別,即可公開以檔案路徑作為輸入參數的公用建構函式,並開啟文字檔以供讀取。
Private _sr As IO.StreamReader Public Sub New(ByVal filePath As String) _sr = New IO.StreamReader(filePath) End Sub
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
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
IEnumerator
介面的Reset
方法會引導迭代器指向文字檔的開頭處,並清除目前的項目值。 將下列程式碼新增至Reset
方法。Public Sub Reset() _ Implements System.Collections.IEnumerator.Reset _sr.DiscardBufferedData() _sr.BaseStream.Seek(0, IO.SeekOrigin.Begin) _current = Nothing End Sub
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()