C#中的枚举器和迭代器
目录
一、可枚举类型和枚举器
1. 枚举器
2. 可枚举类
3. 使用 IEnumerable 和 IEnumerator 案例
4. 泛型枚举接口
二、迭代器
1. 使用迭代器创建枚举器
2. 使用迭代器创建可枚举类
3. 常见的迭代器模式
4. 产生多个枚举类型
5. 将迭代器作为属性
6. 迭代器的实质
一、可枚举类型和枚举器
数组为什么可以被foreach语句处理?原因是数组按需提供了一个枚举器对象。
- foreach结构设计用来和可枚举类型一起使用。
- 可枚举类型可以通过GetEnumerator方法获取对象的枚举器。
- 从枚举器中请求每一项并且把它作为迭代变量,该变量是只读的。

1. 枚举器
枚举器需要实现IEnumerator接口,其中包含三个函数成员:Current、MoveNext、Reset。

因为数组是可枚举类型,所以我们可以通过GetEnumerator方法获取其枚举器对象,来模拟foreach遍历,通过模拟我们可以初步认识枚举器接口中的这三个方法。
internal class Program {static void Main(string[] args){int[] ints = { 5, 6, 7 };IEnumerator enumerator = ints.GetEnumerator();//获取数组的枚举器对象while (enumerator.MoveNext()) //MoveNext方法移动指向数组中下标的指针位置,并判断是否在数组长度范围内,返回一个bool值{int current = (int)enumerator.Current;//获取当前指针指向的元素Console.WriteLine(current);}bool beforeReset = enumerator.MoveNext();Console.WriteLine("位置重置为原始位置之前,enumerator.MoveNext()方法返回:{0}", beforeReset);enumerator.Reset();//将位置重置为原始状态//当位置没有重置时,enumerator.MoveNext()返回fasle,所以不会进入这个while循环,无法遍历while (enumerator.MoveNext()) {int current = (int)enumerator.Current;Console.WriteLine(current);}}}

2. 可枚举类
前面说过数组是可枚举类型,其原因在于数组实现了IEnumerable接口。可枚举类就是指实现了IEnumerable接口的类。

综上,可枚举类型是实现了IEnumerable接口的类,实现了IEnumerable接口中的GetEnumerator方法返回一个枚举器,枚举器是实现了 IEnumerator接口的类,通过实现IEnumerator接口三个方法来访问元素。理解图如下:

3. 使用 IEnumerable 和 IEnumerator 案例
//枚举器class ColorEnumerator : IEnumerator{string[] _colors;int _position = -1;public ColorEnumerator(string[] colors) {_colors = new string[colors.Length];Array.Copy(colors,_colors,colors.Length);}public object Current {get{if (_position == -1)throw new InvalidOperationException();if (_position >= _colors.Length)throw new InvalidOperationException();return _colors[_position];}}public bool MoveNext(){if (_position < _colors.Length - 1){_position++;return true;}elsereturn false; }public void Reset(){_position = -1;}}//可枚举类型class Spectrum : IEnumerable{string[] colors = { "赤", "橙", "黄", "绿", "青", "蓝", "紫" };public IEnumerator GetEnumerator(){return new ColorEnumerator(colors);}}//测试internal class Program {static void Main(string[] args){Spectrum colors = new Spectrum();foreach (string color in colors) Console.WriteLine(color);}}
4. 泛型枚举接口
- 泛型枚举接口的使用和非泛型枚举接口的使用是差不多的
- 非泛型枚举接口的实现不是类型安全的,它们返回object类型的引用,必须强转位实际类型
- 泛型枚举接口的实现是类型安全的,它们返回的是实际类型的对象,而非object基类的引用
- 应该尽量使用泛型枚举接口

二、迭代器
- 迭代器可以代替我们手动编码的可枚举类和枚举器。
- 迭代器块是有一个或多个yield语句的代码块。
- 迭代器块描述了希望编译器为我们创建的枚举器类的行为。
- 迭代器块可以是方法主体、访问器主体或运算符主体。
- 迭代器块中有两个特殊语句:
i. yield return语句指定了序列中返回的下一项
ii.yield break语句指定在序列中没有其他项
1. 使用迭代器创建枚举器
代码示例:
class MyClass {public IEnumerator<string> GetEnumerator() //返回枚举器{return BlackAndWhite();}public IEnumerator<string> BlackAndWhite() //迭代器块{yield return "赤";yield return "橙";yield return "黄";yield return "绿";yield return "青";yield return "蓝";yield return "紫";}} //测试internal class MyTest{static void Main(string[] args){MyClass myClass = new MyClass();foreach (string element in myClass)Console.WriteLine(element);}}
代码图解:

2. 使用迭代器创建可枚举类
代码示例:
class MyClass {public IEnumerator<string> GetEnumerator() //返回枚举器{return BlackAndWhite().GetEnumerator();//返回从可枚举类获取的枚举器}public IEnumerable<string> BlackAndWhite() //返回可枚举类{yield return "赤";yield return "橙";yield return "黄";yield return "绿";yield return "青";yield return "蓝";yield return "紫";}} //测试internal class MyTest{static void Main(string[] args){MyClass myClass = new MyClass();foreach (string element in myClass)//让类本身可枚举Console.WriteLine(element);Console.WriteLine("--------");foreach (string element in myClass.BlackAndWhite())//调用返回可枚举类的方法Console.WriteLine(element);}}
代码图解:

3. 常见的迭代器模式
通过前面的代码案例,创建迭代器可以用来产生可枚举类型和枚举器。总结如下:
- 如果我们创建返回枚举器的迭代器时,必须实现GetEnumerator方法来让类可枚举。
- 如果我们创建返回可枚举类型的迭代器时,我们有两种选择:
选择1:实现GetEnumerator让类本身可枚举
选择2:不实现GetEnumerator让类本身不可枚举,但仍可使用由迭代器产生的可枚举类

4. 产生多个枚举类型
可以在同一个类中创建多个迭代器来产生多个枚举类型。
//注意:该类中没有实现GetEnumerator方法,所以该类本身不可以被枚举,但可以通过迭代器返回的枚举类型进行遍历class MyClass {string[] colors = { "赤", "橙", "黄", "绿", "青", "蓝", "紫" };public IEnumerable<string> PrintOut() //迭代器返回可枚举类型{for (int i = 0; i < colors.Length; i++)yield return colors[i];}public IEnumerable<string> ReversePrintOut() //迭代器返回可枚举类型{for(int i = colors.Length-1; i >= 0; i--)yield return colors[i];}} //测试internal class MyTest{static void Main(string[] args){MyClass myClass = new MyClass();foreach (string color in myClass.PrintOut())Console.WriteLine(color);Console.WriteLine("--------");foreach (string color in myClass.ReversePrintOut())Console.WriteLine(color);}}
5. 将迭代器作为属性
可以将迭代器作为属性。代码示例如下:
class Colors {bool chooseEnumerator;string[] colors = { "赤", "橙", "黄", "绿", "青", "蓝", "紫" };public Colors(bool b) {chooseEnumerator = b;}//根据创建类对象时传入的布尔值控制返回不同的枚举器public IEnumerator<string> GetEnumerator() {return chooseEnumerator ? PrintOut : ReversePrintOut;}public IEnumerator<string> PrintOut //迭代器放置在属性get访问器中{get {for (int i = 0; i < colors.Length; i++)yield return colors[i];}}public IEnumerator<string> ReversePrintOut //迭代器放置在属性get访问器中{get {for (int i = colors.Length - 1; i >= 0; i--)yield return colors[i];}}} //测试internal class MyTest{static void Main(string[] args){Colors colors = new Colors(true);foreach (string color in colors)Console.WriteLine(color);Console.WriteLine("--------");Colors reColors = new Colors(false);foreach (string color in reColors)Console.WriteLine(color);}}
6. 迭代器的实质
- 迭代器需要using指令引入System.Collections.Generic的命名空间。
- 在编译器生成的枚举器中,Reset方法没有实现,调用会抛异常。
- 由编译器生成的枚举器是包含四个状态的状态机。
- Before:首次调用MoveNext的初始状态。
- Running:调用MoveNext后进入这个状态。在这个状态中,枚举器检查并设置下一项的位置,在遇到yield return、yield break或在迭代器体结束时,退出状态。
- Suspended:状态机等待下次调用MoveNext的状态。
- After:没有更多项可以枚举。

(注:本章学习总结自《C#图解教程》)
相关文章:
C#中的枚举器和迭代器
目录 一、可枚举类型和枚举器 1. 枚举器 2. 可枚举类 3. 使用 IEnumerable 和 IEnumerator 案例 4. 泛型枚举接口 二、迭代器 1. 使用迭代器创建枚举器 2. 使用迭代器创建可枚举类 3. 常见的迭代器模式 4. 产生多个枚举类型 5. 将迭代器作为属性 6. 迭代器的实质 一…...
中山大学人工智能学院——考研上岸经验贴
文章目录初试个人基本情况408数学英语政治复试初试 首先是初试成绩,中山大学在2.21号就公布了成绩和排名,这点很不错,有很多学校只公布成绩而没有排名。我的初试总分386,总排名第二,各个科目还是比较平均的࿱…...
ThreeJS-圣诞节表白3D贺卡(三十)
素材分享: 链接: https://pan.baidu.com/s/1l0mZWfkiLaXJfdvZ7XoY8w 提取码: i69h 提前预知: 向下滚动鼠标滑轮切换视角 关键代码: //初始化渲染器 const render new THREE.WebGLRenderer({ //设置抗锯齿,防失真 antialis: …...
040:cesium加载World Terrain地形图
第040个 点击查看专栏目录 本示例的目的是介绍如何在vue+cesium中加载世界地形图。 直接复制下面的 vue+cesium源代码,操作2分钟即可运行实现效果. 文章目录 示例效果配置方式示例源代码(共64行)相关API参考:专栏目标示例效果 配置方式 1)查看基础设置:https://xiaozh…...
逻辑运算和位移指令
逻辑运算指令 AND OR NOT XOR TEST 逻辑位移指令 SHL SHR 算术位移指令 SAL SAR 小循环位移指令 ROL ROR 大循环位移指令 RCL RCR AND 逻辑与指令 汇编格式:AND 目的操作数,源操作数 执行操作:(目的操作数)&…...
大家现在都去做Linux运维了吗?
运维自互联网出现以来,都是以基础技术部门的形式出现在各个互联网公司或者其他需要网络设备的公司里面,职位由来已久,也是多次徘徊在被淘汰的边缘。很多运维人都是靠着自己良好乐观的心态坚持到现在,接受新技术并学习新技术&#…...
Webpack的编译流程是怎么样的?webpack是如何工作的?
Webpack是一款非常流行的前端构建工具,用于将多个模块打包成一个或多个静态资源。它的工作原理是将模块的依赖关系图转化为最终的静态资源。Webpack的编译流程是一个非常复杂的过程,本文将从四个方面详细介绍Webpack的编译流程,分别是入口点分…...
【ZOJ 1151】Word Reversal 题解(字符串+模拟)
问题描述 对于每个单词列表,在不改变单词顺序的情况下,将每个单词反转输出一行。 此问题包含多个测试用例! 多重输入的第一行是整数N,然后是空行,后面跟着N个输入块。每个输入块 采用问题描述中所示的格式。输入块之间…...
Dart语言操作符?和!的用法
一.基本使用 1. ? 操作符跟在类型后面,表示当前变量可为null。 int a null; //这句代码在有空安全时,编译会提示错误如果想给一个变量赋值null要如何处理呢?只需要在类型 后面添加操作符?即可,eg: int? a null…...
聚类 kmeans | 机器学习
聚类 刘建平 1、算法原理: 是一种无监督学习算法,其主要目的是将数据点分为k个簇,距离近的样本具有更高的相似度,距离近的划分为一个簇,一共划分k个簇,**让簇内距离小,簇间距离大。**距离是样…...
求职咨询Job Information
前言 加油 原文 求职咨询常用会话 ❶ I want to apply for a job which enables me to use my major. 我想要申请一个能用到我的专业知识的职业。 ❷ I have the capability of operating the computer. 我有操作电脑的能力。 ❸ My dream is to be an excellent interpret…...
怎么去除pdf文件的水印?好用软件说明
怎么去除pdf文件的水印?在某些情况下,PDF 文件的水印可能会影响文件的可读性和美观度。为了解决这个问题,您可以考虑使用其他方法来标记文档,例如添加页眉或页脚。另一种选择是使用透明度更低的水印,这样它就不会太过分…...
1-ELK+ Elasticsearch+head+kibana、企业内部日志分析系统
ELK:日志收集平台 ELK由ElasticSearch、Logstash和Kibana三个开源工具组成: 概念图 组件介绍 1、Elasticsearch: ElasticSearch是一个基于Lucene的开源分布式搜索服务。只搜索和分析日志 特点:分布式,零配置,自…...
ctfshow愚人杯web复现
easy_signin 题目url base64解码是face.png,尝试flag.txt和flag.php,base64加密后传入都不对,用index.php加密后传入,看源码 将后面的base64解密得到flag 被遗忘的反序列化 源码 <?php# 当前目录中有一个txt文件哦 error_r…...
商品推荐Promoting Products
目录 前言原文内容:推荐常用会话商品推荐常用会话商品推荐常用会话前言 加油 原文内容: ❶ I promise that our product is superior. 我承诺我们的产品比别的家的好。 ❷ Our product is very attractive to young people. 我们的产品很吸引年轻人。 ❸ I want to buy th…...
整懵了,蚂蚁金服4面成功拿下测开offer,涨薪6k,突然觉得跳槽也不是那么难
蚂蚁的面试挺独特的,每轮面试都没有HR约时间,一般是晚上8点左右面试官来一个电话,问是否能面试,能的话开始面,不能就约一个其他时间。 全程4面,前四面技术面,电话面试,最后一面是HR面…...
《扬帆优配》个人养老金投资最新成绩出炉 七成养老FOF跑输基准
自去年底落地以来,个人养老金制度运转已有4个多月。运转以来,设置Y比例的个人养老FOF(基金中的基金)、个人养老金理财、个人养老储蓄、个人养老金稳妥四大产品继续扩容,形成了个人养老金初期的业态样貌。并且历经一季度…...
用Qt编写STM32烧录软件(ISP模式)代码
1.前言 之前写了一篇【用Qt编写STM32烧录软件(ISP模式)】,但是在文中没有具体的实现代码。 现在补上,各位有兴趣的同学可以参考以下。但是代码里面还有很多没有完善的,必定会存在一些bug,目前只是堪堪能用…...
Excel技能之美观排版
一个普通的Excel文件,想要变得好看,除了要掌握相关技能,还要用心。 美观排版,离不开的技能有字体、字体大小、字体颜色、背景色,等等。了解不同的效果用在什么样的场景,才能得心应手,融会贯通&…...
兆芯最新X86 CPU曝光:性能与英特尔/AMD相比,没落后10年
众所周知,在PC领域,X86完全是处于垄断地全的,至少占了90%以上的份额。其它的像MIPS、ARM、RISC-V等等,都不是X86的对手。 这与X86是复杂指令集有关,更与X86绑定了windows操作系统,有坚固的intel联盟有关&am…...
Bypass Paywalls Clean:突破内容壁垒的智能解决方案
Bypass Paywalls Clean:突破内容壁垒的智能解决方案 【免费下载链接】bypass-paywalls-chrome-clean 项目地址: https://gitcode.com/GitHub_Trending/by/bypass-paywalls-chrome-clean 在信息爆炸的今天,你是否曾因学术论文被付费墙阻挡而错失研…...
解决Windows远程桌面连接Ubuntu时xrdp闪退的配置技巧
1. 问题现象与排查思路 最近在帮同事配置Windows远程连接Ubuntu时遇到了一个典型问题:用Windows自带的远程桌面连接工具输入账号密码后,界面闪退无法进入桌面。这种情况在Ubuntu 18.04/20.04/22.04各版本中都可能出现,特别是使用GNOME桌面环…...
lite-avatar形象库部署教程:GPU共享模式下多租户数字人服务隔离方案
lite-avatar形象库部署教程:GPU共享模式下多租户数字人服务隔离方案 1. 项目概述 lite-avatar形象库是一个专业的数字人形象资产管理平台,基于HumanAIGC-Engineering/LiteAvatarGallery构建。这个库提供了150经过预训练的2D数字人形象,专门…...
告别996!用Google Antigravity的Agent-First模式,5分钟搞定React Native与Android原生桥接模块
告别996!用Google Antigravity的Agent-First模式,5分钟搞定React Native与Android原生桥接模块 如果你是一位长期奋战在Android与React Native混合开发一线的工程师,一定对"桥接模块"这个词汇又爱又恨。每当产品经理提出"我们…...
为什么大厂都不用 Apache 了?Nginx 反向代理才是微服务入口
一、前言本文将带大家全面认识Nginx:它是什么、为什么能成为行业主流、核心优势有哪些、能解决哪些实际业务问题,以及和我们熟悉的Apache服务器有什么区别。二、什么是Nginx?Nginx(发音为“engine x”)是由俄罗斯程序员…...
全域软开关直流变换器TPEL论文仿真复现之旅
全域软开关直流变换器 TPEL论文仿真复现最近一头扎进了全域软开关直流变换器的研究里,主要在琢磨TPEL论文相关内容,那仿真复现就成了关键任务。今天就来和大家唠唠这个过程中的酸甜苦辣。 一、全域软开关直流变换器是啥? 简单来说,…...
消息队列的缓冲作用:不止于临时暂存
在分布式系统架构中,消息队列常被提及的一个核心价值是“解耦”。然而,除了降低系统间的直接依赖之外,消息队列还承担着另一个关键角色——缓冲。很多人直观地感受到“消息队列能起到缓冲效果”,但这种缓冲究竟意味着什么…...
LoRA训练助手效果展示:动漫风格迁移作品集
LoRA训练助手效果展示:动漫风格迁移作品集 1. 引言 你是否曾经想过,把自己拍摄的普通照片转换成新海诚风格的唯美画面,或者让日常场景拥有吉卜力工作室的梦幻质感?现在,这一切都不再是梦想。通过LoRA训练助手&#x…...
告别VirtualBox默认20G!保姆级教程:从创建到动态扩容,打造你的专属开发环境
从零规划VirtualBox磁盘空间:开发环境搭建的黄金法则 刚接触VirtualBox的新手开发者们,是否曾在项目进行到一半时突然发现磁盘空间不足?那种被迫中断工作流程去处理存储问题的体验,足以毁掉一天的开发效率。本文将带你从源头规避这…...
3大颠覆:Umi-OCR如何重新定义离线文字识别体验?
3大颠覆:Umi-OCR如何重新定义离线文字识别体验? 【免费下载链接】Umi-OCR Umi-OCR: 这是一个免费、开源、可批量处理的离线OCR软件,适用于Windows系统,支持截图OCR、批量OCR、二维码识别等功能。 项目地址: https://gitcode.com…...
