C#枚举器和迭代器

18. 枚举器和迭代器 当我们使用foreach语句时,这个语句为我们以此取出数组中的每一个元素,允许我们读取它的值。另一方面,迭代器块不是需要在同一时间执行的一串命令式命令,而是描述了希望编译器为我们创建的枚举器类的行为。也就是说,先执行代码块的第一个语句,然后执行后面的语句,最后控制离开块。我们可以把手动编码的可枚举类型和枚举器替换为迭代器生成的枚举器和迭代器。而泛型接口的枚举器是类型安全的,它返回的实际类型的引用。应该实现这些泛型接口。非泛型接口的实现不是类型安全的,它返回的object类型引用,然后必须转换为实际类型。 阅读详情

文章不定时更新,有问题的话欢迎各位dalao指正。

枚举器与可枚举类型

foreach:
我们可以通过foreach来便历或取出数组中的每一个元素:

int[] arr1 = { 10, 11, 12, 13 };
foreach(int item in arr1)
{
    Console.WriteLine($"value:{item}");
}

输出:

value:10
value:11
value:12
value:13

foreach中in后面跟的参数必须是可枚举类型(enumerable type)。
可枚举类型是实现了GetEnumerator方法的类型,而GetEnumerator方法是获取对象枚举器的方法,对于枚举器而言,必须有个方法去获取他。

IEnumerator和IEnumerable

1、IEnumerator

namespace System.Collections
{
    //
    // 摘要:
    //     Supports a simple iteration over a non-generic collection.
    public interface IEnumerator
    {
        //
        // 摘要:
        //     Gets the element in the collection at the current position of the enumerator.
        //
        // 返回结果:
        //     The element in the collection at the current position of the enumerator.
        object? Current { get; }

        //
        // 摘要:
        //     Advances the enumerator to the next element of the collection.
        //
        // 返回结果:
        //     true if the enumerator was successfully advanced to the next element; false if
        //     the enumerator has passed the end of the collection.
        //
        // 异常:
        //   T:System.InvalidOperationException:
        //     The collection was modified after the enumerator was created.
        bool MoveNext();
        //
        // 摘要:
        //     Sets the enumerator to its initial position, which is before the first element
        //     in the collection.
        //
        // 异常:
        //   T:System.InvalidOperationException:
        //     The collection was modified after the enumerator was created.
        void Reset();
    }
}


Current、MoveNext()、Reset是枚举器的三个成员
Current只读,获取集合中枚举器当前位置的元素
MoveNext()将枚举器前进到集合的下一个元素。
Reset()将枚举数设置为其初始位置,即集合中第一个元素之前的位置(-1)。

其实在我们使用foreach语句的时候,C#编译器会生成和下面非常相似的代码。

int[] arr1 = { 10, 11, 12, 13 };
IEnumerator ie = arr1.GetEnumerator();
while (ie.MoveNext())//移到下一项
{
    int i = (int)ie.Current;//获取当前项
    Console.WriteLine($"{i}");
}

10
11
12
13

2、IEnumerable

实现了IEnumerable接口的类叫做可枚举类。
IEnumerable接口中的GetEnumerator 方法返回对象的枚举器。
声明:

class MyClass : IEnumerable//实现IEnumerable接口
{
    public IEnumerator GetEnumerator() {
        //函数返回IEnumerator类型的对象
    }
}

使用IEnumerator和IEnumerable的示例:

class ColorEnumerator : IEnumerator//枚举器
{
    string[] _colors;
    int _position = -1;//设置初始位置在第一个元素之前即为-1
    //构造函数
    public ColorEnumerator(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()
    {//如果可以移动的话向下一个元素位置移动,移动成功返回true,失败返回false
        if (_position < _colors.Length - 1)
        {
            _position++;
            return true;
        }
        else
        {
            return false;
        }
    }
    public void Reset()
    {
        _position = -1;
    }
}
class Spectrum : IEnumerable//可枚举类Spectrum,他的枚举器是ColorEnumerator
{
    string[] Colors = { "yellow", "red", "blue", "pink", "purple", "green", "orange" };
    public IEnumerator GetEnumerator()
    {
        return new ColorEnumerator(Colors);
    }
}
class Program
{
    static void Main(string[] args)
    {
        Spectrum spectrum = new Spectrum();
        foreach(string color in spectrum)
        {
            Console.WriteLine(color);
        }
    }
}

输出:

yellow
red
blue
pink
purple
green
orange

泛型枚举接口

IEnumerable< T>和IEnumerator< T>
注意,非泛型接口的实现不是类型安全的,他们返回object类型的引用,然后必须转化为实际类型。

二者中:非泛型接口中IEnumerator的Current属性返回的是object的引用,而IEnumerator< T>的Current属性返回的是实际类型的对象。

IEnumerable< T>接口的GetEnumerator方法返回实现IEnumator< T>的枚举器类的实例。

迭代器:

可枚举类和枚举器我们已经了解了,下面我们介绍迭代器,这是一种创建二者更简单的方式。
迭代器返回一个泛型枚举器,其中yield return声明这是枚举中的下一项。
第一种实现方式:

public IEnumerator<string> BlackAndWhite()
{
    yield return "black";
    yield return "and";
    yield return "white";
}

第二种:

public IEnumerator<string> BlackAndWhite1()
{
    string[] theColors = { "black", "and", "white" };
    for(int i = 0; i < theColors.Length; i++)
    {
        yield return theColors[i];
    }
}

如果方法在第一个yield return中返回,那么之后的语句就永远不会执行。而且枚举器不会一次返回所有元素,每次访问Current属性时返回一个新值。

迭代器块

迭代器块是有一个或多个yield语句的代码块。
迭代器块中,yield return指定序列中返回的下一项,yield break指定在序列中没有其他项。

使用迭代器创建枚举器

使用迭代器创建可枚举类,看代码:

class MyClass
{
    public IEnumerator<string> GetEnumerator()
    {
        //返回枚举器
        return BlackAndWhite();
    }
    //BlackAndWhite方法是一个迭代器块,可以为MyClass类产生返回枚举器的方法
    //
    public IEnumerator<string> BlackAndWhite()//迭代器
    {
        yield return "black";
        yield return "and";
        yield return "white";
    }
}
class Class1
{
    static void Main()
    {
        MyClass mc = new MyClass();
        //mc是枚举器类MyClass的实例
        foreach(string shade in mc)
        {
            Console.WriteLine(shade);
        }
    }
}

输出

black
and
white

创建的类中包含两部分:产生返回枚举器方法的迭代器、返回枚举器的GetEnumerator。

BlackAndWhite迭代器方法返回IEnumerator< string>,MyClass类通过返回由BlackAndWhite返回的对象来实现GetEnumerator方法。

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

使用迭代器创建可枚举类型,先看代码:

class MyClass
{
    public IEnumerator<string> GetEnumerator()
    {
        IEnumerable<string> myEnumerable = BlackAndWhite();//获取可枚举类型
        //获取枚举器
        return myEnumerable.GetEnumerator();
    }
    
    //返回可枚举类型
    public IEnumerable<string> BlackAndWhite()//迭代器
    {
        yield return "black";
        yield return "and";
        yield return "white";
    }
}
class Class1
{
    static void Main()
    {
        MyClass mc = new MyClass();
        //mc是枚举器类MyClass的实例
        foreach (string shade in mc)
        {
            Console.WriteLine(shade);
        }
        Console.WriteLine();
        //mc.BlackAndWhite是类枚举器方法
        foreach (string shade in mc.BlackAndWhite())
        {
            Console.WriteLine(shade);
        }
    }
}

输出:

black
and
white

black
and
white

在Main中我们可以直接使用类的实例,也可以直接调用BlackAndWhite方法,因为它返回的是可枚举类型。

常见迭代器模式

常见的有两种:
1、枚举器的迭代器模式
2、可枚举类型的迭代器模式
其实就是上面例子中的使用迭代器创建枚举器和使用迭代器创建可枚举类型

产生多个可枚举类型

下面的代码中specrum类中有两个可枚举类型的迭代器,一个正序输出数组中的颜色,另一个逆序。
注意:虽然两个方法返回可枚举类型,但是类本身不是可枚举类型,因为它没有实现GetEnumerator

class Specrum
{
    string[] colors = { "violet", "blue", "yellow", "pink", "black", "orange" };
    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 Class2
{
    static void Main()
    {
        Specrum sp = new Specrum();
        foreach(string color in sp.UVtoIR())
        {
            Console.WriteLine(color);
        }
        Console.WriteLine();
        foreach (string color in sp.IRtoUV())
        {
            Console.WriteLine(color);
        }
    }
}

输出:

violet
blue
yellow
pink
black
orange

orange
black
pink
yellow
blue
violet

将迭代器作为属性

代码描述
1、使用迭代器来产生具有两个枚举器的类
2、迭代器如何能实现为属性而不是方法

class Special
{
    bool _listFromUVtoIR;
    string [] colors = { "violet", "blue", "yellow", "pink", "black", "orange" };
    //构造方法
    public Special(bool listFromUVtoIR)
    {
        _listFromUVtoIR = listFromUVtoIR;
    }
    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 Class3
{
    static void Main()
    {
        Special s1 = new Special(true);
        Special s2 = new Special(false);
        foreach(string color in s1)
        {
            Console.WriteLine(color);
        }
        Console.WriteLine();
        foreach(string color in s2)
        {
            Console.WriteLine(color);
        }
    }
}

输出:

violet
blue
yellow
pink
black
orange

orange
black
pink
yellow
blue
violet

上面的代码声明两个属性来定义两个不同的枚举器,GetEnumerator方法根据_listFromUVtoIR布尔值返回两个枚举器中的一个,当传入true的时候返回UVtoIR枚举器,反之传入IRtoUV枚举器

综上

手动实现迭代器是很麻烦的,我们可以借助yield语句简化迭代。

在实际开发中也有使用迭代器的场景:从时间段中迭代日期、迭代读取文件中的每一行、使用迭代块和迭代条件来对集合进行进行惰性过滤(这一点LINQ也可以做到,在后面的文章中会介绍LINQ)。

C#对很多的设计模式都有间接实现,我们要学会这些设计模式才能更好地运用C#语言带给我们的便捷。

C# 迭代器与枚举器的深入探索 本文深入探讨了C#语言中迭代器块的概念、工作原理以及如何在实际开发中运用。通过对迭代器块、枚举器接口、可枚举接口和迭代器的yield语句等核心概念的解读,文章旨在帮助开发者更好地理解和使用C#中的集合操作。 阅读详情

相关推荐

深入理解C#中的迭代器与枚举器

本文深入探讨了C#编程语言中迭代器和枚举器的概念、实现与应用。通过分析迭代器块的特性、yield语句的作用以及枚举器和可枚举对象的创建和管理,我们能够更好地理解如何在C#中处理有序值序列,实现延迟执行和资源管理。...

weixin_31688273的博客 440

C#基础学习--枚举器和迭代器

枚举器和迭代器(枚举器和可枚举类型,IEnumerator接口,IEnumerable接口,泛型枚举接口,迭代器,常见的迭代器模式,产生多个可枚举类型,迭代器的实质)

zaizai1007的博客 831

MAX30102传感器实战:从寄存器配置到Arduino心率血氧监测

本文详细介绍了MAX30102传感器在Arduino平台上的实战应用,从寄存器配置到心率血氧监测的全过程。通过清晰的代码示例和实用技巧,帮助开发者快速实现脉搏血氧仪功能,包括I2C通信、FIFO数据处理及算法实现,适用于健康监测和可穿戴设备开发。

weixin_29182481的博客 511

C#中的枚举器和迭代器

主要介绍了可枚举类型、枚举器以及迭代器之间的关系,案例包括可枚举类和枚举器的使用,以及如何使用迭代器创建可枚举类型和枚举器。

weixin_66598341的博客 1053

从原理到实践:MIPI D-PHY v1.2的HS-Deskew功能全解析(附时序图详解)

本文深入解析MIPI D-PHY v1.2的HS-Deskew功能,从技术背景到实现机制,详细介绍了其在2.5Gbps高速传输中的关键作用。通过时序图详解和实测数据,帮助工程师理解并应用这一技术,解决信号完整性问题,提升系统可靠性。

weixin_30300225的博客 308

Android perfetto 简介

android linux 性能优化 原理及操作

求变,从思想开始 3479

浅分享关于枚举器的一些知识

说到枚举,我想大伙最熟悉的就是foreach语句。它为我们依次取出了数组中的每一个元素,允许我们读取它的值(但不允许我们修改,后面会分析为什么)。 现在让我们一起浅浅扒开foreach的面纱0 - 0。 首先先介绍两个关键名称和两个关键接口: 关键名称:“可枚举类型”和“枚举器”。 关键接口:"IEnumerator"和"IEnumerable"。 其中"IEnumerator"和“枚举器”相关联,"IEnumerable"和“可枚举类型”相关联 接下来的内容我们主要会围绕这四个关键谈论。 一

du1912894329的博客 815

刷爆 Java 基础!1000 道经典面试题精解第一篇【序列化】

自定义序列化 / 反序列化的实现方式在实现接口的类中,手动重写和方法在方法内优先调用执行默认序列化逻辑;追加自定义逻辑(如敏感数据加密、临时字段赋值、兼容旧版本数据等)。// 序列化ID,保证兼容性// 敏感字段,默认不序列化,自定义处理// 自定义序列化// 执行默认序列化:写入username// 自定义逻辑:密码加密后写入// 自定义反序列化// 执行默认反序列化:读取username// 自定义逻辑:读取加密密码并解密// 模拟加密/解密工具方法。

七叔的博客 191

图解C#高级教程(五):枚举器和迭代器

本章主要介绍 C# 编程语言当中枚举器、可枚举类型以及迭代器相关的知识。

聚焦C++/Python编程语言、大模型应用开发 1866

21、时间触发以太网:通信、诊断、故障隔离与参数化详解

本文详细解析了时间触发以太网(TTEthernet)的核心机制,包括团检测算法、诊断服务、故障隔离策略、配置服务以及协议参数化等内容。TTEthernet是一种专为超高可靠性系统设计的网络技术,广泛应用于航空航天、工业自动化和汽车电子等领域。文章还探讨了其未来发展趋势,如技术创新与生态建设,为开发者、企业和研究机构提供了应用与发展的方向。

efc12345678的博客 110

组态王与DLT645-2007电能表通讯调试总结

组态王与电力协议DLT 645-2007调试总结 DLT 645-2007是多功能电能表通信协议,该标准由中国电力企业联合会2006年提出,该协议为主-从结构的半双工通信方式。手持单元或其它数据终端为主站,多功能电能表为从站。每个多功能电能表均有各自的地址编码。通信链路的建立与解除均由主站发出的信息帧来控制。帧是传送信息的基本单元,每帧由帧起始符、从站地址域、控制码、数据域长度、数据域、帧信息纵向校验码及帧结束符7个域组成,每部分由若干字节组成,帧格式如图1所示。 1.确定设备地址 地址域由 6 个字节构

逍遥子的博客 1万+

C#深入理解枚举器和迭代器

C#深入理解枚举器和迭代器 1、枚举器和可枚举类型 可枚举类型是实现IEnumerable接口的类型, public interface IEnumerable { IEnumerator GetEnumerator(); } IEnumerable接口中有一个方法,用于返回枚举器,枚举器是实现了IEnumerator接口的类型。 IEnumerator接口定义如下: // // 摘要: // 支持对非泛型集合的简单迭代。 [C

目标:MVP 1737

学Simulink——基于Simulink的输入-输出线性化精确转矩控制

本文详细介绍了基于Simulink的永磁同步电机(PMSM)输入-输出线性化(IOL)精确转矩控制方法。针对传统解耦控制在非线性转矩特性下的局限性,提出通过精确反馈线性化实现转矩指令与实际转矩的严格线性关系。文章系统阐述了IOL原理、PMSM控制器推导过程,并提供了完整的Simulink建模步骤,包括电机模型搭建、IOL控制器实现和闭环系统构建。通过仿真对比验证了IOL方法在转矩跟踪精度、动态响应和参数鲁棒性方面的显著优势,最后讨论了工程实现要点和扩展应用方向。该方法为高性能伺服系统开发提供了有效解决方案。

amy_mhd的博客 60

C# 枚举器和迭代器

foreach语句 我们可以用foreach语句来遍历数组中的每一个元素。 为什么数组可以这么做,是因为数组提供了叫做 枚举器的对象 获取一个对象的枚举器方法是 调用对象的GetEnumerator方法,那么如果实现了这个方法的类型叫做 可枚举类型 IEnumerator接口 实现IEnumerator接口的枚举器有三个函数成员 Current是返回序列中当前位置项的属...

131

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

C#-14 枚举器和迭代器

一 枚举器和可枚举类型 当我们为数组使用foreach语句时,这个语句为我们依次取出了数组中的每一个元素。 var arrInt = new int[] { 11, 12, 13, 14 }; foreach (var item in arrInt) { Console.WriteLine(item); } 原因是数组实现了IEnumerable接口,接口提供了一个GetEnumerator方法可以获取一个实现了IEnumerator接口的枚举器对象。 枚举器可以依次返回请求的数组中的..

chinaherolts2008的博客 490

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

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

​ 编译器得到有关枚举项的描述后,会构建包含所有需要的方法(MoveNext)和属性(Current)实现的枚举器类,产生的类被嵌套包含在声明迭代器的类中。​ 可枚举类是指实现了 IEnumerable 接口的类,该接口只有一个成员——GetEnumerator,返回对象的枚举器。​ 对于有枚举器的类型而言,我们使用 GetEnumerator 方法来获取其拥有的枚举器,实现该方法的类型称为。​ 迭代器块描述了希望编译器为我们创建的枚举器类的行为,其中的代码描述了如何枚举元素。或不实现,来让类不可枚举。

zheliku的博客 1517

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

C#学习笔记之——枚举器和迭代器

目录 枚举器和迭代器 枚举器和可枚举类型 IEnumerator接口 IEnumerable接口 泛型枚举接口 迭代器 迭代器块 使用迭代器来创建枚举器 使用迭代器来创建可枚举类型 常见迭代器模式 产生多个可枚举类型 将迭代器作为属性 迭代器的实质 枚举器和迭代器 枚举器和可枚举类型 int[] arr = { 1, 2, 3, 4 }; ...

Alina_catty的博客 841

C# 枚举器和迭代器(泛型枚举接口)

目前我们描述的枚举接口都是非泛型版本。实际上,在大多数情况下你应该使用泛型版本IEnumerab1e和IEnumerator。它们叫作泛型是因为使用了C#泛型(参见第18章),其使用方式和非泛型形式差不多。两者之间的本质差别如下所示。需要重点注意的是,我们目前所看到的非泛型接口的实现不是类型安全的。它们返回object类型的引用,然后必须转化为实际类型。而泛型接口的枚举器是类型安全的,它返回实际类型的引用。如果要创建自己的可枚举类,应该实现这些泛型接口。

钢铁男儿 696

C#图解教程笔记17-枚举器和迭代器

我们可以使用foreach语句遍历数组中的元素,什么数组可以这么做?原因是数组可以按需提供一个叫作枚举器的对象,枚举器可以依次返回请求的数组中的元素。对于有枚举器的类型而言,必须有一种方法来获取它。获取对象举器的方法是调用对象的GetEnumerator方法,实现该方法的类型叫做可枚举类型。foreach结构设计用来和可枚举类型一起使用。

S_Yu_Tong的博客 1015

C# 枚举器和迭代器(枚举器和可枚举类型)

在第13章中,我们已经知道可以使用foreach语句来遍历数组中的元素。本章会进一步探讨数组,看看为什么它们可以被foreach语句处理。我们还会研究如何用迭代器为用户自定义的类增加这个功能。当为数组使用foreach语句时,这个语句为我们依次取出了数组中的每一个元素,允许我们读取它的值。例如,如下的代码声明了一个有4个元素的数组,然后使用foreach来循环打印item为什么数组可以这么做?原因是数组可以按需提供一个叫作枚举器(enumerator)的对象。

钢铁男儿 375

C#图解教程 第十八章 枚举器和迭代器

枚举器和迭代器枚举器和可枚举类型第12章中,我们看到可以用foreach语句遍历数组。在本章,我们会进一步探讨数组,来看看为什么它们可以被foreach语句处理。我们还会研究如何使用迭代器为用户自定义类增加该功能。foreach语句数组foreach语句为我们依次取出数组中的每个元素。int[] arr1={10,11,12,13}; foreach(int item in arr1) { ...

HJH's Blog 643
上一篇: C#事件(基础)
下一篇: [pyqt5]问题记录
吊云
博客等级 码龄6年 73粉丝 10原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值