当前位置: 首页 > news >正文

C# IEnumerator,IEnumerable ,Iterator

IEnumerator 枚举器接口

在C#语言中,大部分以“I”字母开头命名的都是接口,所以情理之中,IEnumerator也是一个接口。

对于面向对象语言来说,接口就是一份“协议”,它定义了一组方法、属性和事件的契约,任何类、结构体或枚举只要符合这个契约,就可以被认为实现了该接口,可以被贴上一个标签,标签上写着这个东西是实现了XX功能的

IEnumerator 是所有非泛型枚举器的基接口。其泛型等效项是 System.Collections.Generic.IEnumerator<T> 接口。其继承了IEnumerator,屏蔽(mask)了基类的Current成员,迭代器返回的值就是泛型类型

看起来IEnumerator这个协议的要求并不复杂,仅仅需要实现三件事:Current属性、MoveNext方法、Reset方法,但是事实上这三部分已经让其具备了一个基本功能:迭代。

我们可以通过Current获取到当前的内容,并通过MoveNext移动到下一个内容的位置,然后继续通过Current获取到当前的内容,最后通过Reset重置。这也是迭代器设计模式的常规思路,对外我们可以不暴露这个迭代的具体过程而只是不停地返回迭代的结果。

在MS官方文档中也给出了自行实现枚举器的示例(注意这不是迭代器,因为方法体中没有yield关键字),这样就可以循环访问(通过枚举器)自定义的集合

using System;
using System.Collections;// Simple business object.
public class Person
{public Person(string fName, string lName){this.firstName = fName;this.lastName = lName;}public string firstName;public string lastName;
}// Collection of Person objects. This class
// implements IEnumerable so that it can be used
// with ForEach syntax.
public class People : IEnumerable
{private Person[] _people;public People(Person[] pArray){_people = new Person[pArray.Length];for (int i = 0; i < pArray.Length; i++){_people[i] = pArray[i];}}// Implementation for the GetEnumerator method.IEnumerator IEnumerable.GetEnumerator(){return (IEnumerator) GetEnumerator();}public PeopleEnum GetEnumerator(){return new PeopleEnum(_people);}
}// When you implement IEnumerable, you must also implement IEnumerator.
public class PeopleEnum : IEnumerator
{public Person[] _people;// Enumerators are positioned before the first element// until the first MoveNext() call.int position = -1;public PeopleEnum(Person[] list){_people = list;}public bool MoveNext(){position++;return (position < _people.Length);}public void Reset(){position = -1;}object IEnumerator.Current{get{return Current;}}public Person Current{get{try{return _people[position];}catch (IndexOutOfRangeException){throw new InvalidOperationException();}}}
}class App
{static void Main(){Person[] peopleArray = new Person[3]{new Person("John", "Smith"),new Person("Jim", "Johnson"),new Person("Sue", "Rabon"),};People peopleList = new People(peopleArray);foreach (Person p in peopleList)Console.WriteLine(p.firstName + " " + p.lastName);}
}

使用枚举器的例子 

foreach背后的原理

foreach语句中in右侧的集合的类型一定要实现IEnumarable接口。因为该语句会调用Ienumerable接口的其中唯一的GetIEnumarator函数,获取该类型的迭代器(迭代器并不唯一),利用迭代器movenext,current函数对集合进行遍历。

IEnumerable 可枚举接口

只要实现GetEnumerator方法的类型就是可枚举类型,反过来也是对的,即如果类中有GetEnumerator方法,也可以不实现IEnumerable接口就可以使用IEnumerator。

Iterator 迭代器 

诞生原因:

“懒”又成为了动力,为了提供更简单的创建枚举器和可枚举方类型的方式,我们通过迭代器让编译器自动创建他们,这样就不用我们手动编码可枚举类型和枚举器了。这也是你能发现迭代器函数(包含迭代器块的函数)为什么返回一个IEnumerable<>/IEnumerator的原因,通过直接返回的枚举器或通过可枚举类型再手动调用其GetEnumerator方法获取的枚举器,再结合yield背后的状态机,我们能实现迭代的功能

迭代器块

首先了解一个概念:迭代器块(忽略访问器主体和运算符主体)看到方法主体内有至少一个yield关键字,那该方法主体就可以被称为迭代器块。迭代器块与其他代码块不同,其他块包含的语句是被当作命令式的,即按顺序执行并最终离开代码块。迭代器块不需要在同一时间执行,他是声明性的描述了编译器为我们隐式创建枚举器类的行为这也是为什么迭代器块一定方法一定需要返回一个定制的枚举器(也可以返回可枚举类型,通过可枚举类型的GetEnumerator方法获取)

本质

迭代器不是一个类、接口,而是一种设计模式,迭代器允许我们自定义一个枚举器的行为(迭代器中的代码描述了如何枚举元素),编译器得到有关如何枚举项的描述后,使用它来构建包含所有需要的方法和属性实现的枚举器,产生的类被嵌套包含声明迭代器的类中

以下使用迭代器创建枚举器(左),使用迭代器创建可枚举类型(右)

下图为根据我们的描述隐式生成一个实现IEnumerable的类

由此上结论:枚举器 + 状态机 = 迭代器,下图为迭代器的状态机

字面意思理解yield的含义是让出,先让一让但不完全走掉,不像return,直接退出去

yield 语句

yield 语句有以下两种形式:yield return:在迭代中提供下一个值,yield break:显式示迭代结束

Console.WriteLine(string.Join(" ", TakeWhilePositive(new int[] {2, 3, 4, 5, -1, 3, 4})));
// Output: 2 3 4 5Console.WriteLine(string.Join(" ", TakeWhilePositive(new int[] {9, 8, 7})));
// Output: 9 8 7IEnumerable<int> TakeWhilePositive(IEnumerable<int> numbers)
{foreach (int n in numbers){if (n > 0){yield return n;}else{yield break;}}
}

实例分析

foreach没有立刻执行代码块中的指令,而是跳转到函数语句中,但是这个函数在一开始就执行一遍了,这次不是在执行函数,而是执行函数内隐式声明了的枚举器函数

如前面的示例所示,当开始对迭代器的结果进行迭代时,在隐式获取的枚举器函数的movenext返回值为true后,函数迭代器会一直执行,直到到达第一个 yield return 语句为止。 迭代器的执行会暂停并记录退出的位置,调用方会获得第一个迭代值()并处理该值。 在后续的每次迭代中(即再次调用movenext函数并返回true时)迭代器的执行都会在导致上一次挂起的 yield return 语句之后恢复,并继续执行(打印了Iterator:yield),直到到达下一个 yield return 语句为止。 当控件到达迭代器或 yield break 语句的末尾时,迭代完成。

yield的优点


Reference

IEnumerable Interface (System.Collections) | Microsoft Learn

漫画秒懂 Unity 的协程与迭代器(Coroutine 与 Enumerator) - 知乎

yield 语句 - 在迭代器中提供下一个元素 - C# reference | Microsoft Learn

相关文章:

C# IEnumerator,IEnumerable ,Iterator

IEnumerator 枚举器接口 在C#语言中&#xff0c;大部分以“I”字母开头命名的都是接口&#xff0c;所以情理之中&#xff0c;IEnumerator也是一个接口。 对于面向对象语言来说&#xff0c;接口就是一份“协议”&#xff0c;它定义了一组方法、属性和事件的契约&#xff0c;任…...

Nginx在Windows上和Linux上(Docker启动)分别配置基本身份认证示例

场景 Nginx代理的资源或网站等&#xff0c;url直接暴露有风险&#xff0c;需要添加身份认证&#xff0c;即输入用户名密码后才能成功访问。 注&#xff1a; 博客&#xff1a;霸道流氓气质-CSDN博客 实现 Windows上配置Nginx实现基本身份认证 修改nginx的配置文件 添加基…...

让SQL更优雅!深入浅出【公用表表达式(CTE)】语法及实战案例

全文目录&#xff1a; 开篇语&#x1f31f; 前言&#x1f4dc; 目录&#x1f4a1; 什么是CTE&#xff1f;&#x1f3a8; CTE的语法与结构&#x1f4a5; 使用场景&#xff1a;CTE何时更香&#xff1f;&#x1f3ac; CTE实战案例案例1&#xff1a;统计每个部门的平均薪资案例2&am…...

快递物流查询API接口如何用PHP调用

在现代商业中&#xff0c;供应链的协同运作至关重要。 快递物流查询API接口可以实现供应商、电商平台、物流企业和消费者之间的信息无缝对接&#xff0c;各方能够及时获取快递物流信息&#xff0c;从而更好地协调生产、销售和配送等环节&#xff0c;提高整个供应链的效率和效益…...

【vue2.0入门】vue基本语法

目录 引言一、页面动态插值1. 一般用法 二、计算属性computed三、动态class、style绑定四、条件渲染与列表渲染五、事件处理六、表单输入绑定七、总结 引言 本系列教程旨在帮助一些零基础的玩家快速上手前端开发。基于我自学的经验会删减部分使用频率不高的内容&#xff0c;并不…...

Dubbo使用Nacos作为注册中心

使用 Nacos 作为注册中心实现自动服务发现 本示例演示 Nacos 作为注册中心实现自动服务发现&#xff0c;示例基于 Spring Boot 应用展开&#xff0c;可在此查看 完整示例代码 1 基本配置 1.1 增加依赖 增加 dubbo、nacos-client 依赖&#xff1a; <dependencies><…...

【面试分享】xshell连接Linux服务器22端口执行命令top期间的技术细节和底层逻辑

通过SSH客户端&#xff08;如Xshell&#xff09;连接到服务器的22端口并执行top命令&#xff0c;涉及多个技术细节和底层逻辑。以下是对这一过程的详细解释&#xff1a; 一、技术细节 SSH协议&#xff1a; SSH&#xff08;Secure Shell&#xff09;是一种网络协议&#xff0c;…...

stm32以太网接口:MII和RMII

前言 使用stm32和lwip进行网络通信开发时&#xff0c;实现结构如下&#xff1a; 而MII和RMII就是stm32与PHY芯片之间的通信接口&#xff0c;类似于I2C、UART等。 stm32以太网模块有专用的DMA控制器&#xff0c;通过AHB接口将以太网内核和存储器相连。 数据发送时&#xff0c;…...

ChromeDriver 官方下载地址_测试自动化浏览器驱动

大家在做selenium自动化测试时&#xff0c;需要下载谷歌浏览器驱动&#xff0c;可以从以下官网地址下载 &#xff08;1&#xff09; ChromeDriver 下载地址1 http://chromedriver.storage.googleapis.com/index.html 这个地址最后版本到 114.0.5735.90 &#xff08;2&#…...

力扣 LeetCode 206. 反转链表(Day2:链表)

解题思路&#xff1a; pre &#xff0c;cur双指针 需要通过tmp暂存cur的下一个位置&#xff0c;以方便cur的下一步移动 class Solution {public ListNode reverseList(ListNode head) {ListNode pre null;ListNode cur head;while (cur ! null) {ListNode tmp cur.next;c…...

kafka消费数据太慢了,给优化下

原代码 public class KafkaConsumerDemo {public static void main(String[] args) {int numConsumers 5; // 增加消费者的数量for (int i 0; i < numConsumers; i) {new Thread(new KafkaConsumerThread()).start();}}static class KafkaConsumerThread implements Runn…...

ASUS/华硕灵耀X双屏Pro UX8402Z 原厂Win11-22H2系统 工厂文件 带ASUS Recovery恢复

华硕工厂文件恢复系统 &#xff0c;安装结束后带隐藏分区&#xff0c;一键恢复&#xff0c;以及机器所有驱动软件。 系统版本&#xff1a;windows11 原厂系统下载网址&#xff1a;http://www.bioxt.cn 需准备一个20G以上u盘进行恢复 请注意&#xff1a;仅支持以上型号专用…...

【含开题报告+文档+PPT+源码】基于springboot的毕业设计选题管理系统

开题报告 毕业设计选题作为高校教学环节中的重要一环&#xff0c;其选题质量和管理效率直接关系到学生毕业设计的质量和毕业要求的达成。然而&#xff0c;传统的选题管理方式往往存在信息不对称、流程繁琐、效率低下等问题&#xff0c;无法满足高校教学管理现代化、信息化的需…...

fastadmin常用操作

数据库中遇到的操作 查询字段是json的某个值 $map[json_extract(goods, "$.brand_id")] (int)$params[brand_id]; //获取数据库中某个字段是json中得某个值&#xff0c;进行查询&#xff0c;goods是表中字段&#xff0c;brand_id是json中要查詢的字段。数据类型一定…...

IPguard与Ping32:谁是企业数据防泄密的最佳选择?

在当前信息化快速发展的背景下&#xff0c;企业数据安全已成为公司运营中最重要的议题之一。为了防止数据泄漏&#xff0c;越来越多的企业开始依赖专业的加密软件来进行防护。今天&#xff0c;我们对比了两款业内领先的加密软件——IPguard和Ping32&#xff0c;帮助您选择最适合…...

C++20新特性的补充讲解

C20 标志着 C 语言的一次重要更新&#xff0c;除了 Concepts、Ranges、协程等被广泛讨论的特性外&#xff0c;还有许多值得注意的改进。本文将详细探讨其他一些核心新特性&#xff0c;包括 constexpr 扩展、新增的 std::format、std::span、std::bit 操作、原子智能指针、char8…...

uni-app移动端与PC端兼容预览PDF文件

过程遇到的问题 1、如果用的是最新的版本的pdfjs的话&#xff0c;就会报Promise.withResolvers 不是一个方法的错误&#xff0c;原因是Promise.withResolvers是ES15新特性&#xff0c;想了解可参考链接&#xff0c;这里的解决方案是将插件里的涉及到Promise.withResolvers的地…...

Elman 神经网络算法详解

Elman 神经网络算法详解 一、引言 Elman 神经网络作为一种经典的递归神经网络&#xff08;RNN&#xff09;&#xff0c;在处理动态系统和时间序列数据方面具有独特的优势。它通过特殊的结构设计&#xff0c;能够有效地捕捉数据中的时间依赖关系&#xff0c;在语音识别、自然语…...

卓胜微嵌入式面试题及参考答案(2万字长文)

freeRTOS 任务是怎么调度的? 在 freeRTOS 中,任务调度主要是基于优先级的抢占式调度。每个任务都有一个优先级,系统会根据任务的优先级来决定哪个任务获得 CPU 的使用权。 当一个高优先级的任务准备运行,并且当前运行的任务优先级较低时,高优先级任务会抢占 CPU。例如,假…...

【Python】爬虫使用代理IP

1、代理池 IP 代理池可以理解为一个池子&#xff0c;里面装了很多代理IP。 池子里的IP是有生命周期的&#xff0c;它们将被定期验证&#xff0c;其中失效的将被从池子里面剔除池子里的ip是有补充渠道的&#xff0c;会有新的代理ip不断被加入池子中池子中的代理ip是可以被随机…...

浅谈 React Hooks

React Hooks 是 React 16.8 引入的一组 API&#xff0c;用于在函数组件中使用 state 和其他 React 特性&#xff08;例如生命周期方法、context 等&#xff09;。Hooks 通过简洁的函数接口&#xff0c;解决了状态与 UI 的高度解耦&#xff0c;通过函数式编程范式实现更灵活 Rea…...

C++初阶-list的底层

目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...

云计算——弹性云计算器(ECS)

弹性云服务器&#xff1a;ECS 概述 云计算重构了ICT系统&#xff0c;云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台&#xff0c;包含如下主要概念。 ECS&#xff08;Elastic Cloud Server&#xff09;&#xff1a;即弹性云服务器&#xff0c;是云计算…...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

HashMap中的put方法执行流程(流程图)

1 put操作整体流程 HashMap 的 put 操作是其最核心的功能之一。在 JDK 1.8 及以后版本中&#xff0c;其主要逻辑封装在 putVal 这个内部方法中。整个过程大致如下&#xff1a; 初始判断与哈希计算&#xff1a; 首先&#xff0c;putVal 方法会检查当前的 table&#xff08;也就…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别

【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而&#xff0c;传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案&#xff0c;能够实现大范围覆盖并远程采集数据。尽管具备这些优势&#xf…...

书籍“之“字形打印矩阵(8)0609

题目 给定一个矩阵matrix&#xff0c;按照"之"字形的方式打印这个矩阵&#xff0c;例如&#xff1a; 1 2 3 4 5 6 7 8 9 10 11 12 ”之“字形打印的结果为&#xff1a;1&#xff0c;…...