(九)枚举器和迭代器(2)

news/2023/12/5 11:09:09

一、Enumerator 接口

实现了 IEnumerator 接口的枚举器包含3个函数成员:Current、MoveNext 以及 Reset。

1)Current: 返回序列中当前位置项的属性。

  • 只读属性。
  • 返回 Object 类型。可以返回对应的可枚举类型。

2)MoveNext: 枚举器位置移到集合中下一个元素的方法,返回值为 bool,返回值代表位置的状态,是有效位置还是到末尾项的位置。

  • 返回值为 true,说明新的位置是有效的。
  • 返回值为 false,说明新的位置无效,也有可能到了末尾的位置。
  • 枚举器的原始位置在序列中的第一项之前,因此 MoveNext 必须在第一次使用 Current 之前调用。

3)Reset: 把位置重置为原始状态的方法。

请添加图片描述

二、IEnumberable 接口

可枚举类型之所以能通过 GetEnumerator 方法来获取枚举器对象,是因为 IEnumberable 接口里有这个方法,并且可枚举类型实现了 IEnumberable 接口的类。

class MyClass:IEnumerable
{
public IEnumberator GetEnumerator{...}
}

请添加图片描述

使用 IEnumerbalbe 和 IEnumerator 的示例

    //枚举器class ColorEnumberator : IEnumerator{string[] colors;int position = -1;public ColorEnumberator(string[] theColors){colors = new string[theColors.Length];for (int i = 0; i < theColors.Length; i++)colors[i] = theColors[i];}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;}else{return false;}}public void Reset(){position = -1;}}//可枚举类型class  Spectrum:IEnumerable{string[] Colors = { "violet", "blue", "cyan", "green", "yellow", "orange", "red" };public IEnumerator GetEnumerator(){//创建自定义枚举器ColorEnumberator 对象return new ColorEnumberator(Colors);//此处断点调试}}class Program{static void Main(string[] args){Spectrum spectrum = new Spectrum();foreach (string color in spectrum)//此处断点调试,可进入到GetEnumerator() 方法里Console.WriteLine(color);Console.ReadKey();}}

IEnumerable: 用来实现可枚举类型对象,并调用 GetEnumerator 方法获取枚举器
IEnumerator: 用来自定一个枚举器类型

三、泛型枚举接口

泛型枚举接口:

IEnumerable<T>
IEnumerator<T>

对于非泛型接口:

  • IEnumerable 接口的 GetEnumerator 方法返回实现 IEnumerator 的枚举器实例;
  • 实现 IEnumerator 的类实现了 Current 属性,它返回 object 类型的引用,然后我们必须把它转化为对象的实际类型。

泛型接口继承自非泛型接口。对于泛型接口形式:

  • IEnumerable <T> 接口的GetEnumerator 方法返回实现 IEnumertor <T> 接口的枚举器的实例。
  • 实现 IEnumertor <T> 的类实现了 Current 属性,它返回实际类型的实例,而不是object 基类的引用。
  • 这些是协变接口,所以它们实际声明为 IEnumerable < out T> 、 IEnumertor < out T>

类型安全
==非泛型接口的实现不是类型安全的。==它们返回 object 类型的引用,然后必须转化为实际类型。
泛型接口的枚举器是类型安全的,它返回实际类型的引用。
图19-7实现 IEnumerator 接口的类的结构

请添加图片描述

图19-7实现 IEnumerable 接口的类的结构

请添加图片描述

四、迭代器

编译器为我们创建了枚举器和可枚举类型,这种结构叫作迭代器

1、声明迭代器

以下是声明两个迭代器的版本

版本1

//声明迭代器 BlackAndWhite;
public IEumerator<string> BlackAndWhite()
{
yield return "black";//如果没有返回,则执行下一条返回语句
yield return "gray";
yield return "white";
}

版本2

//声明迭代器 BlackAndWhite;
public IEumerator<string> BlackAndWhite()
{
string[] theColors = { "black","gray","white" };
for(int i = 0;i < theColors.Length;i++)
yield return theColors[i];
//如果没有返回则再遍历一次;如果能返回,则结束后续迭代
}

yield return 在枚举器中的作用: 每遍历一次访问 Current 属性时,就要返回一个新值,而不是一次返回所有元素。

2、迭代器块

迭代器块是有一个或多个 yield 语句的代码块。

迭代器块有3种:

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

迭代器块与其他代码块不同。其他块包含的语句被当作是命令式的。也就是说,先执行代码块的第一个语句,然后执行后面的语句,最后控制离开块。

另一方面,迭代器不是需要再同一时间执行的一串命令式命令,而是声明性的,它描述了希望编译器为我们创建的枚举器类的行为。迭代器块中的代码描述了如何枚举元素。

迭代器块有两个特殊语句。

  • yield return 语句指定了序列中要返回的下一项。
  • yield break 语句指定再序列中没有其他项。

可以让迭代器产生枚举器或可枚举类型

//产生枚举器的迭代器
public IEnumerator<string> IteratorMethod()
{
yield retrun...
}//产生可枚举类型的迭代器
public IEnumerable<string> IteratorMethod()
{
yield retrun...
}

使用迭代器来创建枚举器

 class  MyClass{public int  CurrentIndex = 0;//由于实现了GetEnumerator方法,MyClass 类是可枚举类型public IEnumerator<string> GetEnumerator(){return BlackAndWhite();//返回枚举器}public IEnumerator<string> BlackAndWhite()//迭代器{yield return "black ";yield return "gray ";yield return "white " ;//返回枚举器 IEnumerator<string>}}class Program{static void Main(string[] args){MyClass mc = new MyClass();foreach (string shade in mc)Console.WriteLine(shade);Console.ReadKey();}}

输出结果:

black
gray
white

问题: BlackAndWhite 的作用是通过迭代器来创建一个枚举器,可 foreach 语句为什么会遍历输出 BlackAndWhite 方法里的所有返回值呢?

要想回答这个问题,以下我就通过把 BlackAndWhite 方法调整为:
(在 MyClass 类添加了一个 变量 CurrentIndex,以追踪枚举器获取当前值的位置)

  public IEnumerator<string> BlackAndWhite()//迭代器{//第一次遍历,返回含有black内容的枚举器yield return "black " + CurrentIndex;CurrentIndex++;//第二次遍历,返回含有gray内容的枚举器yield return "gray " + CurrentIndex;CurrentIndex++;//第三次遍历,返回含有white内容的枚举器yield return "white " + CurrentIndex;//该注释掉的代码块,其输出结果跟以上不含有CurrentIndex的代码相同//迭代器里含有字符串数组,是可枚举类型中的内容//string[] theColors = { "black", "gray", "white" };//for (int i = 0; i < theColors.Length; i++)//    yield return theColors[i];}

输出结果:

black 0
gray 1
white 2

由以上的代码可知,foreach 每次遍历访问元素,首先都要调用一次 BlackAndWhite 方法,然后再返回其枚举器,执行 当前 yield return 之后,不会继续执行下行代码,直到下一次遍历的时候,才会继续下一条 yield return 语句。

请添加图片描述

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

  class  MyClass{public IEnumerator<string> GetEnumerator(){IEnumerable<string> mEnumerable = BlackAndWhite();//获取可枚举类型return mEnumerable.GetEnumerator();//获取枚举器}//IEnumerable<string>:返回可枚举类型public IEnumerable<string> BlackAndWhite(){yield return "black";yield return "gray";yield return "white";}}class Program{static void Main(string[] args){MyClass mc = new MyClass();foreach (string shade in mc)//使用类对象Console.Write($"{ shade }  ");foreach (string shade in mc.BlackAndWhite())//使用枚举器方法Console.Write($"{ shade }  ");Console.ReadKey();}}

输出结果:

black gray white black gray white

请添加图片描述

3、总结常见迭代器模式

枚举器的迭代器模式

class MyClass
{
public IEnumerator<string> GetEnumerator()
{
return IteratorMethod();
}public IEnumerator<string> IteratorMethod()
{
...
yield return ...;
}
}void Main()
{
MyClass mc = new MyClass();
foreach(string x in mc)
...
}

可枚举类型的迭代器模式

class MyClass
{
public Ienumerator<string> GetEnumerator()
{
return IteratorMethod().GetEnumerator();
}public IEnumerable<string> IteratorMethod()
{
yield return...
}void Main()
{
MyClass mc = new MyClass();
foreach(var string x in mc)
...foreach(string x in mc.IteratorMethod())
...}
}

4、产生多个可枚举类型

使用迭代器来产生具有两个可枚举类型的类。

Specturm 类有两个可枚举类型的迭代器 —— 一个从紫外线到红外线枚举光谱中的颜色,而另一个以逆序进行枚举。Spectrum 类不是可枚举类型,但是它有两个方法返回可枚举类型。

    class Spectrum{string[] colors = { "violet", "blue", "cyan", "green", "yellow", "orange", "red" };//返回可枚举类型public IEnumerable<string> UVtoIR(){for (int i = 0; i < colors.Length; i++)yield return colors[i];}//返回可枚举类型public IEnumerable<string> IRtoUV(){for (int i = colors.Length - 1; i >= 0; i--)yield return colors[i];}}class Program{static void Main(string[] args){Spectrum spectrum = new Spectrum();foreach (string color in spectrum.UVtoIR())Console.Write($"{ color }   ");Console.WriteLine();foreach (string color in spectrum.IRtoUV())Console.Write($"{ color }   ");Console.WriteLine();Console.ReadKey();}}

输出结果:

violet blue cyan green yellow orange red
red orange yellow green cyan blue violet

5、将迭代器作为属性

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

  • 1)使用迭代器来产生具有两个枚举器的累;
  • 2)演示迭代器如何实现为属性而不是方法。
   class Spectrum{bool _listFromUVtoIR;string[] colors = { "violet", "blue", "cyan", "green", "yellow", "orange", "red" };public Spectrum(bool lisatFromUVtoIR){_listFromUVtoIR = lisatFromUVtoIR;}public IEnumerator<string> GetEnumerator(){return _listFromUVtoIR ? UVtoIR : IRtoUV;}public IEnumerator<string> UVtoIR{get{for (int i = 0; i < colors.Length; i++)yield return colors[i];}}public IEnumerator<string> IRtoUV{get{for (int i = colors.Length - 1; i >= 0; i--)yield return colors[i];}}}class Program{static void Main(string[] args){Spectrum startUV = new Spectrum(true);Spectrum startIR = new Spectrum(false);foreach (string color in startUV)Console.Write($"{ color }   ");Console.WriteLine();foreach (string color in startIR)Console.Write($"{ color }   ");Console.WriteLine();Console.ReadKey();}}

输出结果:

violet blue cyan green yellow orange red
red orange yellow green cyan blue violet

6、迭代器的实质

迭代器的其他重要事项

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

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

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

如果状态机在 Before 或 Suspended 状态时调用了 MoveNext 方法,就转到了 Running 状态。在Running 状态中,它检测集合的下一项并设置位置。

如果有更多项,状态机会转入 Suspended 状态;如果没有更多项,它转入并保持在 After 状态。

请添加图片描述


http://www.ppmy.cn/news/387492.html

相关文章

华为设备密码大全,网工必看!

大家好&#xff0c;我的网工朋友 华为设备&#xff0c;公众号说过很多了&#xff0c;路由器、交换机、防火墙、服务器啥都给你分析过。 这些设备&#xff0c;你在刚使用的时候&#xff0c;一定都会先看看说明书啥的&#xff0c;但初始状态的用户名和密码&#xff0c;你有没有…

Win10 IE11浏览器,您正在查看的页使用 Java,Microsoft 网站提供有关 Java 支持的更多信息 解决

最近工作需要支持下IE11浏览器&#xff0c;使用java applet控件。 以前IE10及以下版本都比较正常&#xff0c;但是IE11会出现一些比较奇怪的现象。 记录下解决的方法和过程&#xff0c;便于有需要的同学自取。 1.首先是报错&#xff0c;如下图所示&#xff1b;这个网上搜索了…

计算机管理主分区改成逻辑分区,如何在Win7系统中将主分区更改为逻辑分区?...

win7论坛中的一个问题: 如何将win7系统中的主分区更改为逻辑分区&#xff1f; 编辑的答案: 看到此问题&#xff0c;编辑器还遇到了类似的问题“如何将win7系统中的主分区更改为逻辑驱动器&#xff1f;” win7的伟大神灵告诉我&#xff0c;逻辑驱动器就是我们通常所说的硬盘分区…

WIN7封装教程2018系列(一)—虚拟机与母盘安装

前言&#xff1a;本教程以WIN7-64位旗舰版进行封装讲解&#xff0c;32位亦可以按照此教程封装&#xff0c;只是有些软件&#xff0c;运行库&#xff0c;更新文件等等可能不通用。本教程和去年WIN10教程的风格一样&#xff0c;以系列为顺序&#xff0c;用图文的方式呈现给大家。…

[原创]Win7SP1的映像DISM集成+kb3125574,打造Win7SP2

缘起 手中有一个cn_windows_7_ultimate_with_sp1_x64_dvd_u_677408.iso(大小3.18GB)&#xff0c;发布日期应该是2011年&#xff0c;安装好之后有无数个补丁要打&#xff0c;很麻烦。微软于2016.5.16发布了KB3125574便捷更新包Microsoft Update Catalog。要是将改补丁包集成部署…

U盘安装Win7操作系统

玩转Windows7系统镜像四部曲 Step 1: 下载Win7 ISO系统镜像 温馨提示:请您尽量选用Win7之家​提供的官方原版镜像安装,因为正版比各种所谓的“精简版、纯净版”等修改过的系统更安全! 那么该如何选择32位和64位呢? 如果您内存小于4GB,一般选择32位版;如果内存大于4GB,…

教程 | win7环境下MySQL详细安装过程

昨晚进行MySQL安装&#xff0c;中途出现了一系列大大小小的问题&#xff0c;期间去相关论坛找解决方案&#xff0c;但一直都不行。后来看到一个视频教程&#xff0c;安装一路畅通&#xff0c;做点笔记。 视频链接&#xff1a;https://www.bilibili.com/video/BV1Jt4y197UT?fr…

小米笔记本安装原生win7

对小米笔记本垂涎已久终于在媳妇的认同下买了13.3的i7顶配本本, 心动不已, 入手后发现随机附带的是win10家庭版, 且win10对CorelDraw软件的支持目前还不是很好(不是菜单显示不出来, 就是安装后导致windows 的启动按钮无法弹出),于是乎就一直思索着将原版系统换成Win7系统, 但由…

该按钮可以重启计算机,win7指定快捷键和重启快捷键是什么

篇一:Windows7的关机快捷键和重启快捷键 Windows7的关机快捷键和重启快捷键是什么? 这个问题在软媒论坛里面问的还真比较多,早期操作系统如XP的关机快捷键是“Win键 U U”,但是到了Windows7和Vista时代,很多朋友找不到这个了。 好吧,今天Win7之家给大家说下两种方法: 一…

在Win7下彻底删除驱动|禁止自动安装之解决方法

http://www.blhekai.com/remove-drivers-win7-stop-automatic-installa-solution.html 众所周知&#xff0c;Windows 7或者Vista的自动安装驱动很酷很方便&#xff0c;但当你有时候因为某种原因&#xff0c;不想让系统自动给设备安装驱动&#xff0c;可不可以这样呢&#xff1f…

win7系统 .NET framework 4.0 安装失败回滚

[教程] win7系统 .NET framework 4.0 安装失败回滚 有的软件需要支持.net 4.0 需要安装.NET framework 4.0&#xff0c;最后是.NET framework 4.0 安装失败回滚。 查看安装日志&#xff0c;有报“安装时发生严重错误”。 第一种方法&#xff1a; 如果是win7的系统&#xff0c;…

win7原版系统安装及基本设置

hello 大家好&#xff0c;我是mahz&#xff01; 今天&#xff0c;我要安装的是一台学校老师的笔记本。这是一台XPS15&#xff0c;配置为I5、4g、500g硬盘。 实话说&#xff0c;现在安装win7的机会越来越少了&#xff0c;因为win10现在安装起来太方便了&#xff0c;驱动的兼容性…

win7系统提示0x80072F8F错误代码的解决方法(刷新你的认知)

时间2020-5-4&#xff0c;因为我认为这个问题是具有时效性的&#xff0c;时间长了不一定还能帮到你。 前提条件&#xff1a; 1.正版win7系统&#xff08;我的是win7_中文版_sp1&#xff09; 2.正版激活码 3.激活工具激活后&#xff0c;更改时间会提示再次激活。 首先&#…

教你使用U盘重装windows7系统的详细步骤

现在已经没有办法在官方安装到window7系统了&#xff0c;但是还是有很多的历史版本可以在网上找到。win7系统对于一些比较老旧的电脑来说还是挺不错的&#xff0c;因为它用起来会更流畅。那么今天我们来看看如何U盘重装windows7系统的方法吧。 准备工作&#xff1a; 1、U盘一个…

CentOS7 win7 双系统安装

历时三天&#xff0c;终于解决了关于CentOS7&#xff0c;win7系统双系统的安装问题。 说来也惭愧&#xff0c;早在三个月之前&#xff0c;我就装过centos6.5,当时都花费了一两天。最后倒是给装好了&#xff0c;无线网卡驱动也给安装好了&#xff0c;WiFi也搜到了&#xff0c;诡…

win7 蓝屏:stop 0x0000006b解决方法

同学win7遇到了这个问题&#xff0c;因为重装系统觉得很麻烦&#xff0c;而且装了之后很多的软件都要重新装才行&#xff0c;所以上网研究了一下。 在windowsclub上面关于这个问题的解释是&#xff1a; This occurs because the Bootcat.cache file, located at %SystemRoot%…

w ndows7旗舰版镜像下载,win7旗舰版32位原版iso

win7旗舰版32位原版iso装机系统是一款非常值得用户们尝试使用的一款优秀佳作,强大的稳定性是win7旗舰版32位原版iso系统区别于同类型定位系统的最大特色,对win7旗舰版32位原版iso系统感兴趣的小伙伴们快来下载体验吧。 win7旗舰版32位原版iso特色: 1、默认关闭局域网共享、远…

Vmware15虚拟机安装win7镜像

Vmware15虚拟机安装win7镜像 下载镜像地址创建虚拟机安装win7镜像 下载镜像地址 由于微软已经停止对win7提供支持&#xff0c;所以在官网只能下载到win10的镜像 下面的网址是win7旗舰版的镜像下载地址 http://www.windows7en.com/ 创建虚拟机 下载镜像时&#xff0c;可以先创…

z77 intel750装win7简略攻略

本文将在你只有非uefi u盘系统的前提下完成uefi gpt win7的安装 1.主板固件升级&#xff0c;我的是华擎z77 extreme6&#xff0c;官方网上2.80E的固件里写了nvme但是不是很明确能否启动&#xff0c;下载之&#xff0c;用MMTool.exe载入那个Z77EXT62.80E&#xff08;无扩展名&a…

vostro3070装win7_戴尔3070MT装win7系统详细教程(包括BIOS设置USB驱动)

戴尔3070MT是一款台式电脑,该系列产品选用Intel 酷睿第九代CPU。默认是自带了win10系统,近期有许多网友改了win7后发现进不了系统,因为默认设置戴尔OptiPlex 3070MT安裝win7是没法使用的,除开BIOS时要关掉安全性运行外,也要选用win7自带了相关最新驱动的才可以,否则安裝后…
最新文章