编程.NET 库开发人员 / 中级 VB.NET 开发者

描述在 Visual Basic 中实现和使用迭代器(Enumerable / Iterator 术语)的方法,以及如何创建自定义可枚举集合。

用 Hintsage AI 助手通过面试

答案。

问题背景
迭代器自编程语言中出现集合后,成为了一个重要组成部分,这些集合需要被遍历。从 .NET 版本开始,Visual Basic 支持 IEnumerable/IEnumerator,使得创建自定义可枚举集合成为可能,并能够使用 For Each 循环以便捷和简洁的方式访问集合内容。

问题
许多开发者仅使用内置集合,或未正确实现 IEnumerable 接口,从而无法通过 For Each 遍历对象。此外,通过 Current 和 MoveNext 实现和维护迭代器状态也较为复杂。

解决方案
为支持迭代,类型实现 IEnumerable 接口(或泛型 IEnumerable(Of T))。实现 GetEnumerator 函数以返回实现了 IEnumerator 的对象。在 VB.NET 中,还可以使用 Yield 关键字实现迭代器方法(从 VB 2015 开始),这大大简化了遍历的实现。

代码示例:

Imports System.Collections Public Class SimpleCollection Implements IEnumerable Private arr() As Integer = {1, 2, 3} Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator For Each i In arr Yield i ' 针对 VB 2015+ Next End Function End Class ' 使用: For Each n As Integer In New SimpleCollection() Console.WriteLine(n) Next

关键特性:

  • 为支持 For Each,类型必须实现 IEnumerable / IEnumerable(Of T)。
  • 实现通常委托给现有集合,或创建自己的集合。
  • 使用 Yield 可以轻松创建迭代器;如果没有 Yield,则必须手动实现 IEnumerator。

刁钻问题。

支持 For Each 是否必须同时实现两个接口:IEnumerable 和 IEnumerator?

不,足够实现 IEnumerable 并返回兼容的迭代器。如果您想实现遍历,可以使用嵌套集合中已有的 IEnumerator。但是,完全自定义实现需要两个部分的实现。

代码示例:

Public Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator Return arr.GetEnumerator() ' 使用内置数组迭代器 End Function

在 Visual Basic 中是否可以仅显式实现 IEnumerable?

是的,您可以显式实现接口,特别是在您的集合已经有自己的 GetEnumerator,并且您希望隐藏接口的实现时。无法直接调用这样的 GetEnumerator,但 For Each 可以正常工作。

代码示例:

Public Function IEnumerable_GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator ' ...方法体 End Function

如果 GetEnumerator 返回 Nothing,会发生什么?

在首次尝试遍历集合时将抛出异常,因为 For Each 期望有效的迭代器。

常见错误和反模式

  • 错误实现 MoveNext/Current 方法,导致元素被跳过或发生错误。
  • 在调用之间破坏迭代器状态(例如,未在重新遍历时重置索引)。
  • 缺失或不正确的 StopIteration 异常。

生活中的例子

否定案例

集合类未实现 IEnumerable,而单独创建了 Next 方法来遍历元素。因此,它无法在通用方法或 For Each 中使用,需要编写额外的方法。

优点:

  • 快速实现以满足特定需求。

缺点:

  • 无法使用 For Each 和 LINQ。
  • 失去与 .NET API 的兼容性。

正面案例

开发者实现了 IEnumerable 和 GetEnumerator,现在集合可以与 For Each 一起使用,并与 LINQ 集成。

优点:

  • 与 .NET 基础设施超级兼容。
  • 通用性和易用性。

缺点:

  • 手动实现时,代码量稍多,直到出现 Yield。