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 设置。
创建可枚举类
创建可枚举类项目
|
-
在 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()
原文链接:https://docs.microsoft.com/zh-cn/previous-versions/visualstudio/visual-studio-2010/dd145423(v=vs.100)