Walkthrough: Implementing IEnumerable(Of T) in Visual Basic
The IEnumerable<T> interface is implemented by classes that can return a sequence of values one item at a time. The advantage of returning data one item at a time is that you do not have to load the complete set of data into memory to work with it. You only have to use sufficient memory to load a single item from the data. Classes that implement the IEnumerable(T)
interface can be used with For Each
loops or LINQ queries.
For example, consider an application that must read a large text file and return each line from the file that matches particular search criteria. The application uses a LINQ query to return lines from the file that match the specified criteria. To query the contents of the file by using a LINQ query, the application could load the contents of the file into an array or a collection. However, loading the whole file into an array or collection would consume far more memory than is required. The LINQ query could instead query the file contents by using an enumerable class, returning only values that match the search criteria. Queries that return only a few matching values would consume far less memory.
You can create a class that implements the IEnumerable<T> interface to expose source data as enumerable data. Your class that implements the IEnumerable(T)
interface will require another class that implements the IEnumerator<T> interface to iterate through the source data. These two classes enable you to return items of data sequentially as a specific type.
In this walkthrough, you will create a class that implements the IEnumerable(Of String)
interface and a class that implements the IEnumerator(Of String)
interface to read a text file one line at a time.
Note
Your computer might show different names or locations for some of the Visual Studio user interface elements in the following instructions. The Visual Studio edition that you have and the settings that you use determine these elements. For more information, see Personalizing the IDE.
Creating the Enumerable Class
Create the enumerable class project
In Visual Basic, on the File menu, point to New and then click Project.
In the New Project dialog box, in the Project Types pane, make sure that Windows is selected. Select Class Library in the Templates pane. In the Name box, type
StreamReaderEnumerable
, and then click OK. The new project is displayed.In Solution Explorer, right-click the Class1.vb file and click Rename. Rename the file to
StreamReaderEnumerable.vb
and press ENTER. Renaming the file will also rename the class toStreamReaderEnumerable
. This class will implement theIEnumerable(Of String)
interface.Right-click the StreamReaderEnumerable project, point to Add, and then click New Item. Select the Class template. In the Name box, type
StreamReaderEnumerator.vb
and click OK.
The first class in this project is the enumerable class and will implement the IEnumerable(Of String)
interface. This generic interface implements the IEnumerable interface and guarantees that consumers of this class can access values typed as String
.
Add the code to implement IEnumerable
Open the StreamReaderEnumerable.vb file.
On the line after
Public Class StreamReaderEnumerable
, type the following and press ENTER.Implements IEnumerable(Of String)
Visual Basic automatically populates the class with the members that are required by the
IEnumerable(Of String)
interface.This enumerable class will read lines from a text file one line at a time. Add the following code to the class to expose a public constructor that takes a file path as an input parameter.
Private _filePath As String Public Sub New(ByVal filePath As String) _filePath = filePath End Sub
Your implementation of the GetEnumerator method of the
IEnumerable(Of String)
interface will return a new instance of theStreamReaderEnumerator
class. The implementation of theGetEnumerator
method of theIEnumerable
interface can be madePrivate
, because you have to expose only members of theIEnumerable(Of String)
interface. Replace the code that Visual Basic generated for theGetEnumerator
methods with the following code.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
Add the code to implement IEnumerator
Open the StreamReaderEnumerator.vb file.
On the line after
Public Class StreamReaderEnumerator
, type the following and press ENTER.Implements IEnumerator(Of String)
Visual Basic automatically populates the class with the members that are required by the
IEnumerator(Of String)
interface.The enumerator class opens the text file and performs the file I/O to read the lines from the file. Add the following code to the class to expose a public constructor that takes a file path as an input parameter and open the text file for reading.
Private _sr As IO.StreamReader Public Sub New(ByVal filePath As String) _sr = New IO.StreamReader(filePath) End Sub
The
Current
properties for both theIEnumerator(Of String)
andIEnumerator
interfaces return the current item from the text file as aString
. The implementation of theCurrent
property of theIEnumerator
interface can be madePrivate
, because you have to expose only members of theIEnumerator(Of String)
interface. Replace the code that Visual Basic generated for theCurrent
properties with the following code.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
The
MoveNext
method of theIEnumerator
interface navigates to the next item in the text file and updates the value that is returned by theCurrent
property. If there are no more items to read, theMoveNext
method returnsFalse
; otherwise theMoveNext
method returnsTrue
. Add the following code to theMoveNext
method.Public Function MoveNext() As Boolean _ Implements System.Collections.IEnumerator.MoveNext _current = _sr.ReadLine() If _current Is Nothing Then Return False Return True End Function
The
Reset
method of theIEnumerator
interface directs the iterator to point to the start of the text file and clears the current item value. Add the following code to theReset
method.Public Sub Reset() _ Implements System.Collections.IEnumerator.Reset _sr.DiscardBufferedData() _sr.BaseStream.Seek(0, IO.SeekOrigin.Begin) _current = Nothing End Sub
The
Dispose
method of theIEnumerator
interface guarantees that all unmanaged resources are released before the iterator is destroyed. The file handle that is used by theStreamReader
object is an unmanaged resource and must be closed before the iterator instance is destroyed. Replace the code that Visual Basic generated for theDispose
method with the following code.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
Using the Sample Iterator
You can use an enumerable class in your code together with control structures that require an object that implements IEnumerable
, such as a For Next
loop or a LINQ query. The following example shows the StreamReaderEnumerable
in a LINQ query.
Dim adminRequests =
From line In New StreamReaderEnumerable("..\..\log.txt")
Where line.Contains("admin.aspx 401")
Dim results = adminRequests.ToList()