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.
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 Visual Studio Settings.
Creating the Enumerable Class
To 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 to StreamReaderEnumerable. This class will implement the IEnumerable(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.
To 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 the StreamReaderEnumerator class. The implementation of the GetEnumerator method of the IEnumerable interface can be made Private, because you have to expose only members of the IEnumerable(Of String) interface. Replace the code that Visual Basic generated for the GetEnumerator 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
To 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 the IEnumerator(Of String) and IEnumerator interfaces return the current item from the text file as a String. The implementation of the Current property of the IEnumerator interface can be made Private, because you have to expose only members of the IEnumerator(Of String) interface. Replace the code that Visual Basic generated for the Current 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 the IEnumerator interface navigates to the next item in the text file and updates the value that is returned by the Current property. If there are no more items to read, the MoveNext method returns False; otherwise the MoveNext method returns True. Add the following code to the MoveNext 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 the IEnumerator interface directs the iterator to point to the start of the text file and clears the current item value. Add the following code to the Reset 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 the IEnumerator interface guarantees that all unmanaged resources are released before the iterator is destroyed. The file handle that is used by the StreamReader object is an unmanaged resource and must be closed before the iterator instance is destroyed. Replace the code that Visual Basic generated for the Dispose 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()