C#快速入门(十):枚举

一、枚举

1.1 IEnumerator 接口

IEnumerator 接口是用于支持集合类的迭代的接口。它定义了用于访问集合中元素的成员,并提供了一种统一的方式来遍历集合中的元素。

IEnumerator 接口定义了以下成员:

  • Current 属性:获取集合中当前位置的元素。
  • MoveNext() 方法:将迭代器推进到集合中的下一个元素。
  • Reset() 方法:将迭代器重置到集合的开头。

使用 IEnumerator 接口遍历集合:

  • 要使用 IEnumerator 接口遍历集合,首先需要获取集合的 IEnumerator 对象,通常使用集合的 GetEnumerator() 方法来实现。
  • 调用 MoveNext() 方法将迭代器推进到集合中的下一个元素,并返回一个布尔值指示是否还有更多的元素可供遍历。
  • 使用 Current 属性获取当前位置的元素值。
  • 如果需要重新开始遍历,可以调用 Reset() 方法将迭代器重置到集合的开头。

以下是一个示例代码,演示了如何使用 IEnumerator 接口遍历集合:

ArrayList list = new ArrayList();
list.Add("Apple");
list.Add("Banana");
list.Add("Orange");

IEnumerator enumerator = list.GetEnumerator();

while (enumerator.MoveNext())
{
    string fruit = (string)enumerator.Current;
    Console.WriteLine(fruit);
}

enumerator.Reset();

while (enumerator.MoveNext())
{
    string fruit = (string)enumerator.Current;
    Console.WriteLine(fruit);
}

在上述示例中,我们创建了一个 ArrayList 集合,并向其中添加了几个元素。然后,我们使用集合的 GetEnumerator() 方法获取一个 IEnumerator 对象,用于遍历集合。

通过调用 MoveNext() 方法,我们将迭代器推进到集合中的下一个元素。在每次循环中,我们使用 Current 属性获取当前位置的元素值,并将其转换为 string 类型。

在第一个循环结束后,我们调用 Reset() 方法将迭代器重置到集合的开头。然后,再次使用 MoveNext() 和 Current 遍历集合,输出相同的元素。

需要注意的是,迭代器模式在遍历过程中通常会检测集合的结构是否发生了变化,以确保遍历的安全性。如果在遍历过程中修改了集合的结构(如添加、删除元素),可能会引发 InvalidOperationException 异常。因此,在使用迭代器遍历集合时,应注意避免修改集合的结构。

1.2 foreach 语句

foreach 语句是用于遍历集合或数组的循环结构。它提供了一种简洁的方式来逐个访问集合中的元素,而无需使用索引或迭代器。

语法:

foreach (var item in collection)
{
// 执行操作
}

item是一个变量,用于存储集合中的当前元素值。

  • collection 是一个实现了 IEnumerable 或 IEnumerable 接口的集合或数组。
  • 在循环体内,可以对 item 进行操作,访问集合中的元素。

foreach 语句的特点:

  • foreach 语句提供了一种简洁的方式来遍历集合或数组中的元素,无需使用索引或迭代器。
  • foreach 语句会自动处理集合的迭代过程,使得代码更加清晰和易读。
  • foreach 语句在循环开始时会自动获取集合的迭代器,并在每次迭代时更新当前元素。

foreach 语句的底层原理:

  • foreach 语句的底层实现依赖于 IEnumerable 接口和迭代器模式。
  • 集合类或数组类实现了 IEnumerable 或 IEnumerable 接口,该接口定义了 GetEnumerator() 方法,用于返回一个实现了 IEnumerator 或 IEnumerator 接口的迭代器对象。
  • foreach 语句在编译时会被转换为使用迭代器的代码,以实现对集合的遍历。
  • 在循环开始时,foreach 语句会调用集合的 GetEnumerator() 方法获取一个迭代器对象。
  • 然后,使用迭代器的 MoveNext() 方法将迭代器推进到集合中的下一个元素,并使用 Current 属性获取当前元素的值。
  • 在每次循环迭代中,循环体内的代码会对当前元素进行操作。
  • 当迭代结束时,或者遇到 break 语句时,循环退出。

需要注意的是,使用 foreach 语句遍历集合时,不应在循环过程中修改集合的结构(如添加、删除元素),否则可能会引发异常。foreach 语句在循环开始时获取了集合的迭代器,如果在迭代过程中修改了集合的结构,会导致迭代器失效,从而引发 InvalidOperationException 异常。

1.3 yield 语句

yield 语句用于创建迭代器(iterator),它提供了一种简洁的方式来实现可枚举类型(enumerable types)的迭代。通过使用 yield 语句,可以在迭代过程中逐个返回元素,而无需显式地实现迭代器的接口和方法。

语法:

  • yield 语句通常与一个迭代器方法(iterator method)一起使用。迭代器方法是一种特殊的方法,它使用 yield 语句来指示迭代过程的逻辑。
  • IEnumerable<T>:迭代器方法返回类型应为 IEnumerable<T>,表示它可以产生一个序列的元素。
  • yield return:使用 yield return 语句返回序列中的元素。可以在迭代过程中多次使用 yield return 语句返回多个元素。
  • yield break:使用 yield break 语句提前终止迭代,返回到调用代码。

迭代器方法的基本语法如下:

public IEnumerable<T> MyIterator()
{
    // 迭代逻辑
    yield return element;
}

yield 语句的特点:

  • 使用 yield 语句可以使迭代过程更加简洁和易读,避免了手动实现迭代器的复杂性。
  • yield 语句将迭代逻辑与产生序列的元素分离,使代码更加模块化和可维护。

yield 语句的底层原理:

  • yield 语句的底层原理基于状态机(state machine)的概念。
  • 当编译器遇到包含 yield 语句的迭代器方法时,它将生成一个状态机类(state machine class)来处理迭代逻辑。
  • 状态机类实现了 IEnumerator<T> 接口,并使用状态来追踪迭代过程。
  • 每次调用迭代器方法时,会创建一个状态机对象的实例,并返回该实例的迭代器。
  • 在每次调用迭代器的 MoveNext() 方法时,状态机根据当前状态执行相应的逻辑,并返回下一个元素的值。
  • 使用 yield return 语句返回元素时,状态机会将当前状态保存下来,并在下一次调用 MoveNext() 时继续执行。
  • 当遇到 yield break 语句或迭代结束时,状态机停止迭代,并将迭代器标记为结束。

下面是一个使用 yield 语句的简单示例,演示了如何使用 yield 语句来创建一个简单的迭代器方法:

static void Main(string[] args)
{
    foreach (var number in GetNumbers())
    {
        Console.WriteLine(number);
    }
}
public static IEnumerable<int> GetNumbers()
{
    yield return 1;
    yield return 2;
    yield return 3;
}

在上述示例中,GetNumbers() 方法是一个迭代器方法,返回类型为 IEnumerable<int>。通过使用 yield return 语句,每次迭代时返回一个整数元素。在 Main() 方法中,使用 foreach 循环来遍历迭代器方法返回的序列,并输出每个元素的值。
需要注意的是,使用 yield 语句创建的迭代器是惰性求值的。即在迭代过程中,元素是按需生成的,只有在调用者请求时才会计算和返回元素值。这种惰性求值的特性可以提高性能和节省内存,尤其在处理大型数据集时。

  • 26
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值