演练:在 Visual Basic 中实现 IEnumerable(Of T)Walkthrough: Implementing IEnumerable(Of T) in Visual Basic

IEnumerable<T>接口由每次可以返回一项值序列的类实现。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. 实现接口的类 IEnumerable(T) 可用于 For Each 循环或 LINQ 查询。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. 应用程序使用 LINQ 查询从文件返回与指定条件相匹配的行。The application uses a LINQ query to return lines from the file that match the specified criteria. 若要通过使用 LINQ 查询来查询文件的内容,应用程序可以将文件的内容加载到数组或集合中。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. LINQ 查询可以使用可枚举的类(仅返回与搜索条件匹配的值)来查询文件内容。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.

你可以创建一个实现接口的类, IEnumerable<T> 以将源数据作为可枚举数据公开。You can create a class that implements the IEnumerable<T> interface to expose source data as enumerable data. 实现接口的类 IEnumerable(T) 将需要另一个实现接口的类 IEnumerator<T> 来循环访问源数据。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.

在本演练中,您将创建一个实现接口的类 IEnumerable(Of String) 和一个实现 IEnumerator(Of String) 接口的类,以便一次一行地读取一个文本文件。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.

备注

以下说明中的某些 Visual Studio 用户界面元素在计算机上出现的名称或位置可能会不同。Your computer might show different names or locations for some of the Visual Studio user interface elements in the following instructions. 这些元素取决于你所使用的 Visual Studio 版本和你所使用的设置。The Visual Studio edition that you have and the settings that you use determine these elements. 有关详细信息,请参阅个性化设置 IDEFor more information, see Personalizing the IDE.

创建可枚举类Creating the Enumerable Class

创建可枚举类项目Create the enumerable class project

  1. 在 Visual Basic 的 " 文件 " 菜单上,指向 " 新建 ",然后单击 " 项目"。In Visual Basic, on the File menu, point to New and then click Project.

  2. 在“新建项目”对话框的“项目类型”窗格中,确保选中“Windows”。In the New Project dialog box, in the Project Types pane, make sure that Windows is selected. 在“模板”**** 窗格中,选择“类库”****。Select Class Library in the Templates pane. 在“名称”框中,键入 StreamReaderEnumerable,然后单击“确定”。In the Name box, type StreamReaderEnumerable, and then click OK. 将显示新项目。The new project is displayed.

  3. 解决方案资源管理器中,右键单击 Class1 文件,然后单击 " 重命名"。In Solution Explorer, right-click the Class1.vb file and click Rename. 将文件重命名为 StreamReaderEnumerable.vb,然后按 Enter。Rename the file to StreamReaderEnumerable.vb and press ENTER. 重命名文件也会将类重命名为 StreamReaderEnumerableRenaming the file will also rename the class to StreamReaderEnumerable. 此类将实现 IEnumerable(Of String) 接口。This class will implement the IEnumerable(Of String) interface.

  4. 右键单击 "StreamReaderEnumerable" 项目,指向 " 添加",然后单击 " 新建项"。Right-click the StreamReaderEnumerable project, point to Add, and then click New Item. 选择 " " 模板。Select the Class template. 在“名称”框中,键入 StreamReaderEnumerator.vb,然后单击“确定”********。In the Name box, type StreamReaderEnumerator.vb and click OK.

此项目中的第一个类是可枚举类并将实现 IEnumerable(Of String) 接口。The first class in this project is the enumerable class and will implement the IEnumerable(Of String) interface. 此泛型接口实现 IEnumerable 接口,并保证此类的使用者可以访问类型为 String 的值。This generic interface implements the IEnumerable interface and guarantees that consumers of this class can access values typed as String.

添加代码以实现 IEnumerableAdd the code to implement IEnumerable

  1. 打开 StreamReaderEnumerable 文件。Open the StreamReaderEnumerable.vb file.

  2. 在 "之后的行" Public Class StreamReaderEnumerable 中,键入以下各项,然后按 enter。On the line after Public Class StreamReaderEnumerable, type the following and press ENTER.

    Implements IEnumerable(Of String)
    

    Visual Basic 使用接口所需的成员自动填充类 IEnumerable(Of String)Visual Basic automatically populates the class with the members that are required by the IEnumerable(Of String) interface.

  3. 此可枚举类将一次一行地读取文本文件中的行。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
    
  4. 接口方法的实现 GetEnumerator IEnumerable(Of String) 将返回类的新实例 StreamReaderEnumeratorYour implementation of the GetEnumerator method of the IEnumerable(Of String) interface will return a new instance of the StreamReaderEnumerator class. GetEnumerator IEnumerable 可以执行接口的方法 Private ,因为必须仅公开接口的成员 IEnumerable(Of String)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. 将 Visual Basic 为方法生成的代码替换为 GetEnumerator 以下代码。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
    

添加代码以实现 IEnumeratorAdd the code to implement IEnumerator

  1. 打开 StreamReaderEnumerator 文件。Open the StreamReaderEnumerator.vb file.

  2. 在 "之后的行" Public Class StreamReaderEnumerator 中,键入以下各项,然后按 enter。On the line after Public Class StreamReaderEnumerator, type the following and press ENTER.

    Implements IEnumerator(Of String)
    

    Visual Basic 使用接口所需的成员自动填充类 IEnumerator(Of String)Visual Basic automatically populates the class with the members that are required by the IEnumerator(Of String) interface.

  3. 枚举器类打开文本文件,并执行文件 i/o 以读取文件中的行。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
    
  4. Current和接口的属性均 IEnumerator(Of String)IEnumerator 形式返回文本文件中的当前项 StringThe Current properties for both the IEnumerator(Of String) and IEnumerator interfaces return the current item from the text file as a String. Current IEnumerator 可以执行接口的属性 Private ,因为必须仅公开接口的成员 IEnumerator(Of 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. 将为属性生成的代码替换为 Current 以下代码 Visual Basic。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
    
  5. MoveNext接口的方法 IEnumerator 导航到文本文件中的下一项并更新属性返回的值 CurrentThe 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. 如果没有更多要读取的项,则此 MoveNext 方法将返回 False ; 否则,该 MoveNext 方法将返回 TrueIf there are no more items to read, the MoveNext method returns False; otherwise the MoveNext method returns True. 将以下代码添加到 MoveNext 方法中。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
    
  6. Reset接口的方法 IEnumerator 指示迭代器指向文本文件的开头,并清除当前项值。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. 将以下代码添加到 Reset 方法中。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
    
  7. Dispose接口的方法 IEnumerator 保证在迭代器销毁之前释放所有非托管资源。The Dispose method of the IEnumerator interface guarantees that all unmanaged resources are released before the iterator is destroyed. 对象使用的文件句柄 StreamReader 是非托管资源,必须在销毁迭代器实例之前关闭。The file handle that is used by the StreamReader object is an unmanaged resource and must be closed before the iterator instance is destroyed. 将 Visual Basic 为方法生成的代码替换为 Dispose 以下代码。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

你可以在代码中将可枚举的类与需要实现的对象 IEnumerable (如 For Next 循环或 LINQ 查询)的控制结构一起使用。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. 下面的示例显示了 StreamReaderEnumerable LINQ 查询中的。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()

请参阅See also