C# 图解教程 第5版 —— 第19章 枚举器和迭代器

文章目录

    • 19.1 枚举器和可枚举类型
    • 19.2 IEnumerator 接口
    • 19.3 IEnumerable 接口
    • 19.4 泛型枚举接口
    • 19.5 迭代器
      • 19.5.1 迭代器块
      • 19.5.2 使用迭代器来创建枚举器
      • 19.5.3 使用迭代器来创建可枚举类型
    • 19.6 常见迭代器模式
    • 19.7 产生多个可枚举类型
    • 19.8 将迭代器作为属性
    • 19.9 迭代器的实质

19.1 枚举器和可枚举类型

使用 foreach 语句

​ 下面是使用 foreach 语句遍历数组的示例。

image-20231218145254811 image-20231218145309824

​ 数组可以使用这种方式访问的原因是:数组提供了枚举器对象。枚举器知道数组中元素的次序,并依次返回数组中的元素。

​ 对于有枚举器的类型而言,我们使用 GetEnumerator 方法来获取其拥有的枚举器,实现该方法的类型称为可枚举类型。数组就是可枚举类型。

image-20231218145604337
图19.1 枚举器和可枚举类型概览

​ foreach 结构设计用来和可枚举类型一起使用,如下行为会被执行:

  1. 调用 GetEnumerator 方法获取对象的枚举器。
  2. 从枚举起中请求每一项并作为迭代变量(可读不可写)。
image-20231218145810666

19.2 IEnumerator 接口

​ 实现了 IEnumerator 接口的枚举器包含 3 个函数成员:

  1. Current:返回序列中当前位置项的属性。
    • 为只读属性。
    • 返回 object 类型的引用。
  2. MoveNext:是将枚举器位置前进到集合中下一项的方法,返回布尔值。
    • 如果新的位置有效,返回 true。
    • 如果新的位置无效,返回 false。
  3. Reset:将位置重置为原始状态。
    • 枚举器的原始位置在序列中的第一项之前,因此 MoveNext 必须在第一次使用 Current 之前调用。
image-20231218150209771
图19.2 小集合的枚举器

​ 在编写 foreach 循环的时候,C# 编译器将生成与下面十分类似的代码(以 CIL 的形式)。

image-20231218150415769 image-20231218150445568
图19.3 .NET 数组类实现了 IEnumerator

19.3 IEnumerable 接口

​ 可枚举类是指实现了 IEnumerable 接口的类,该接口只有一个成员——GetEnumerator,返回对象的枚举器。

image-20231218150743647
图19.4 GetEnumerator 方法返回类的一个枚举器对象
image-20231218150941982

使用 IEnumerable 和 IEnumerator 的示例

image-20231218151148423 image-20231218151203527

19.4 泛型枚举接口

​ 使用 C# 泛型和非泛型的方式相差不大。

  • 对于非泛型接口形式:
    • IEnumerable 接口的 GetEnumerator 方法返回实现 IEnumerator 的枚举器类实例。
    • 实现 IEnumerator 的类实现了 Current 属性,返回 object 类型的引用,然后将其转化为对象的实际类型。
  • 对于泛型接口形式:
    • IEnumerable<T> 接口的 GetEnumerator 方法返回实现 IEnumerator<T> 的枚举器类实例。
    • 实现 IEnumerator<T> 的类实现了 Current 属性,返回实际类型的实例,而不是 object 基类的引用。
    • 实际上泛型接口的声明是协变的,即 IEnumerable<out T> 和 IEnumerator<out T>,因此这些接口的对象可以是派生的类型。

​ 泛型版本简单易用,但其结构略显复杂。

image-20231218151811109
图19.5 实现 IEnumerable<T> 接口的类的结构

19.5 迭代器

​ 可枚举类和枚举器在 .NET 集合类中被广泛使用,从 C# 2.0 版本开始提供了创建枚举器和可枚举类型更简单的方式,将这种结构称为迭代器

​ 在下面这个示例中:

  • 迭代器返回一个泛型枚举器,该枚举器返回 3 个 string 类型的项。
  • yield return 语句声明这是枚举中的下一项。
image-20231218152306099

​ 下面的方法声明了另一个版本,输出结果与上面相同。

image-20231218152341904

​ 枚举器不会一次返回所有的元素,而是每次访问 Current 属性时返回一个新值。

19.5.1 迭代器块

​ 迭代器块是有一个或多个 yield 语句的代码块,可以是如下任意一种代码块:

  • 方法主体。
  • 访问器主体。
  • 运算符主体。

​ 迭代器块描述了希望编译器为我们创建的枚举器类的行为,其中的代码描述了如何枚举元素。

  1. yield return:指定序列中要返回的下一项。
  2. yield break:指定在序列中没有其他项。

​ 编译器得到有关枚举项的描述后,会构建包含所有需要的方法(MoveNext)和属性(Current)实现的枚举器类,产生的类被嵌套包含在声明迭代器的类中。

image-20231218152851321
图19.6 根据指定的返回类型,可以让迭代器产生枚举器或可枚举类型

19.5.2 使用迭代器来创建枚举器

image-20231218153055412 image-20231218153138376
  • 图中左边演示了返回类型是 IEnumerator<string>。
  • 图中右边演示了它有一个嵌套类实现了 IEnumerator<string>。
image-20231218153301556
图19.7 迭代器块产生了枚举器

19.5.3 使用迭代器来创建可枚举类型

​ 本节例子使用迭代器来创建可枚举类型,而不是枚举器。

  • BlackAndWhite 迭代器方法返回 IEnumerable<string> 而不是 IEnumerator<string>。
  • MyClass 首先调用 BlackAndWhite 方法获取可枚举类型对象,然后调用该对象的 GetEnumerator 方法来获取结果,从而实现 GetEnumerator 方法。
image-20231218154115386
  • 图中左边演示了返回类型是 IEnumerable<string>。
  • 图中右边演示了它有一个嵌套类实现了 IEnumerator<string> 和 IEnumerable<string>。
image-20231218154355077
图19.8 编译器生成的类是可枚举类型并返回一个枚举器。编译器还生成了方法 BlackAndWhite,返回可枚举对象

19.6 常见迭代器模式

  1. 实现返回枚举器的迭代器。

    通过实现 GetEnumerator 方法让类可枚举,它返回由迭代器返回的枚举器。

  2. 实现返回可枚举类型的迭代器。

    实现 GetEnumerator 来让类本身可枚举;或不实现,来让类不可枚举。

image-20231218155301997
图19.9 常见的迭代器模式

19.7 产生多个可枚举类型

​ Spectrum 类有两个可枚举类型的迭代器,但类本身不可枚举,因为没有实现 GetEnumerator 方法。

image-20231218155441822 image-20231218155514934

19.8 将迭代器作为属性

​ 本示例演示两个方面的内容:

  1. 使用迭代器产生两个枚举器的类。
  2. 演示迭代器如何实现为属性,而不是方法。

​ 这段代码声明了两个属性来定义两个不同的枚举器。GetEnumerator 方法根据 _listFromUVtoIR 布尔变量的值返回两个枚举器中的一个。如果 _listFromUVtoIR 为 true,则返回 UVtoIR 枚举器;否则,返回 IRtoUV 枚举器。

image-20231218160024023 image-20231218160102546 image-20231218160121319

19.9 迭代器的实质

​ 有关迭代器的其他重要事项:

  • 迭代器需要 System.Collections.Generic 命名空间,因此需要使用 using 指令进行引入。
  • 在编译器生成的枚举器中,不支持 Reset 方法。Reset 是接口需要的方法,所以实现了它,但调用时总是抛出 System.NotSupportedException 异常。

​ 在后台,编译器生成的枚举器类总是包含 4 个状态的状态机。

  1. Before:首次调用 MoveNext 的初始状态。
  2. Running:调用 MoveNext 后进入这个状态。
    • 枚举器检测并设置下一项的位置。
    • 遇到 yield return、 yield break 或在迭代器中结束时,退出 Running 状态。
  3. Suspended:状态机等待下次调用 MoveNext 的状态。
  4. After:没有更多项可以枚举的状态。
image-20231218161138144
图19.10 迭代器状态机

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/285196.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

阿里云|人工智能(AI)技术解决方案

函数计算部署Stable Diffusion AI绘画技术解决方案 通过函数计算快速部署Stable Diffusion模型为用户提供快速通过文字生成图片的能力。该方案通过函数计算快速搭建了AIGC的能力&#xff0c;无需管理服务器等基础设施&#xff0c;专注模型的能力即可。该方案具有高效免运维、弹…

Excel小技能:excel如何将数字20231211转化成指定日期格式2023/12/11

给了一串数字20231211&#xff0c;想要转成指定格式的日期格式&#xff0c;发现设置单元格格式为指定日期格式不生效&#xff0c;反而变成很长很长的一串#这个&#xff0c;如图所示&#xff1a; 其实&#xff0c;正确的做法如下&#xff1a; 1&#xff09;打开数据功能界面&am…

[ CTF ]【天格】战队WriteUp-第七届“强网杯”全国安全挑战赛

第七届“强网杯”全国安全挑战赛 2023.12.16~2023.12.17 文章目录 【Misc】Pyjail ! Its myFILTER !!!easyfuzz谍影重重2.0签到Pyjail ! Its myRevenge !!!server_8F6C72124774022B.py 问卷调查 【Reverse】ezre 【Web】happygame 【强网先锋】石头剪刀布TrieSpeedUpezreez_fmt…

Python 全栈体系【四阶】(七)

第四章 机器学习 六、多项式回归 1. 什么是多项式回归 线性回归适用于数据呈线性分布的回归问题。如果数据样本呈明显非线性分布&#xff0c;线性回归模型就不再适用&#xff08;下图左&#xff09;&#xff0c;而采用多项式回归可能更好&#xff08;下图右&#xff09;。例…

回归预测 | MATLAB实现GA-LSSVM基于遗传算法优化最小二乘向量机的多输入单输出数据回归预测模型 (多指标,多图)

回归预测 | MATLAB实现GA-LSSVM基于遗传算法优化最小二乘向量机的多输入单输出数据回归预测模型 &#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现GA-LSSVM基于遗传算法优化最小二乘向量机的多输入单输出数据回归预测模型 &#xff08;多指标&#…

支持向量机(SVM):高效分类的强大工具

文章目录 前言1. SVM的基本原理1.1 核心思想1.2 支持向量1.3 最大化建模1.4 松弛变量1.5 核函数 2. SVM与逻辑回归的区别和联系2.1 区别2.2 联系 3. SVM的应用领域3.1 图像分类3.2 文本分类3.3 生物信息学3.4 金融领域3.5 医学诊断 4. SVM的优势与挑战4.1 优势4.1.1 非线性分类…

K8s内容器拓扑图工具

1.背景&#xff1a;随着线上容器越来越多&#xff0c;需要一个可视化的方式展示各个容器之间的拓扑图。 2.需求&#xff1a;轻量级&#xff0c;部署方便。 3.部署 helm repo add groundcover https://helm.groundcover.com/ helm repo update helm install caretta --namespa…

使用ffmpeg将图片合成为mp4

首先在在图片文件夹输入cmd 这里确保已经安装ffmpeg并配置好环境变量。 然后这是我的文件夹目录&#xff1a; 将21张图片合成为mp4视频 这里使用如下命令&#xff1a; ffmpeg -framerate 1 -start_number 0 -i %d.png -c:v libx264 -pix_fmt yuv420p output.mp4 -framerat…

SpringBoot Whitelabel Error Page 报错--【已解决】

springboot 报错信息如下 这个报错页面就是个404 &#xff0c;代表你访问的url 没有对应的的requestmapping 其实没啥影响的一个问题&#xff0c;但是看到Error 就是不爽&#xff0c;改了他丫的 解决方法如下 一、调整application.properties配置【治标不治本】 server.err…

docker在线安装minio

1、下载最新minio docker pull minio/minio 2、在宿主机创建 /usr/local/data/miniodocker/config 和 /usr/local/data/miniodocker/data,执行docker命令 docker run -p 9000:9000 -p 9090:9090 --name minio -d --restartalways -e MINIO_ACCESS_KEYminio -e MINIO_SECRET_K…

Spring6 代理模式-动态代理

需求&#xff1a; 生产代理对象的工厂类&#xff1a; package aop.proxy;import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays;public class ProxyFactory {//目标对象private Object t…

100GPTS计划-AI写诗PoetofAges

地址 https://chat.openai.com/g/g-Cd5daC0s5-poet-of-ages https://poe.com/PoetofAges 测试 创作一首春天诗歌 创作一首夏天诗歌 创作一首秋天诗歌 创作一首冬天诗歌 微调 诗歌风格 语气&#xff1a;古典 知识库