c#列举和迭代器

列举 - Enumeration

迭代器是一个值序列(集合)上的一个只读且只向前移动的游标。迭代器要么实现了IEnumerator接口,要么实现了IEnumerator<T>接口。

从技术的角度看,如果一个对象有MoveNext方法以及Current属性,那么我们就可以将其看作一个迭代器。

我们可以使用foreach语句去迭代一个可列举对象。可迭代的对象其实就是一个序列的逻辑体现。可列举的对象不但自身就是一个游标,而且它还可以生成一个游标迭代自己。因此,可列举的对象有两个特性

  • 实现IEnumerator接口,或实现IEnumerator<T>接口
  • 有一个方法GetEnumerator,该方法返回一个迭代器

列举模式:

class Enumerator
{
    public IteratorVariableType Current {get {...}}   
    public bool MoveNext() {...}
}

class Enumerable
{
    public Enumerator GetEnumerator() {...}
}
Enumeration pattern

为了更好的理解上面的概率和模式,我们来看下面的两个例子

foreach (char c in "CSharp")
    Console.WriteLine(c);
Sample 1
using (var enumerator = "CSharp".GetEnumerator())
{
    while (enumerator.MoveNext()) {
        Console.WriteLine(enumerator.Current);
    }
}
Sample 2

Sample1采取了foreach这样的高级方式去迭代字符串(因为字符串类实现了CharEnumerator);而Sample2则使用了底层的方式完成对字符串的迭代。 对于Sample我们使用了using语句,这是因为CharEnumerator实现了IDisposable接口,下面的代码显示了CharEnumrator的大部分代码(来自微软官方)

public sealed class CharEnumerator : IEnumerator, IDisposable
{
    private String str;
    private int index;
    private char currentElement;

    internal CharEnumerator(String str)
    {
        this.str = str;
        this.index = -1;
    }


    public bool MoveNext()
    {
        if (index < (str.Length - 1))
        {
            index++;
            currentElement = str[index];
            return true;
        }
        else
            index = str.Length;
        return false;

    }

    public void Dispose()
    {
        if (str != null)
            index = str.Length;
        str = null;
    }


    public char Current
    {
        get
        {
            return currentElement;
        }
    }

    public void Reset()
    {
        currentElement = (char)0;
        index = -1;
    }
}
CharEnumerator

初始化集合

我们可使用一行语句实例一个可列举的对象。比如:IList<Int> list = new List<int>{1,2,3};编译时,编译器会自动翻译为:

IList<Int> list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
Translated Code

这是因为该列举对象实现了IEnumerable接口,而且还包含了Add方法。
为了验证此点,我们可以通过查看IL代码的方式来确认:

IL_0000:  nop
  IL_0001:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1<int32>::.ctor()
  IL_0006:  stloc.1
  IL_0007:  ldloc.1
  IL_0008:  ldc.i4.1
  IL_0009:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
  IL_000e:  nop
  IL_000f:  ldloc.1
  IL_0010:  ldc.i4.2
  IL_0011:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
  IL_0016:  nop
  IL_0017:  ldloc.1
  IL_0018:  ldc.i4.3
  IL_0019:  callvirt   instance void class [mscorlib]System.Collections.Generic.List`1<int32>::Add(!0)
  IL_001e:  nop
  IL_001f:  ldloc.1
  IL_0020:  stloc.0
  IL_0021:  call       string [mscorlib]System.Console::ReadLine()
IL Code

迭代器 - Iterator

既然foreach可应用于列举,那么一个列举可以生成一个迭代器。很绕口很困惑是吧,我们先来看下面的例子:使用迭代器返回斐波纳契数列

static IEnumerable<int> Fibonacci(int number)
{ 
    for(int i=0, prevFib=1, curFib=1;i<number;i++)
    {
        yield return prevFib;
        int newFib = prevFib + curFib;
        prevFib = curFib;
        curFib = newFib;
    }
}

// test
static void Main(string[] args)
{
    foreach (int f in Fibonacci(10))
        Console.WriteLine(f);

    Console.ReadLine();
}
Fibonacci

请注意,在上面的代码中,我们使用了yield return。那么它和return有什么区别呢?
return:从方法中返回一个值
yield return:从当前的迭代器中生成下一个元素。yield语句每执行一次,程序的控制权就退还给调用者,而被调用者的状态仍然保留,这就使得方法在调用者列举下一个元素的时候能继续执行。被调用者的状态的生命周期取决于列举,正因为如此,当调用者完成列举后,被调用者的状态得以释放。

迭代器语法

迭代器可以是包含了一个或多个yield语句的方法、属性、或所引器。迭代器必须返回下面四个类型之一:IEnumerable, IEnumerable<T>, IEnumerator, IEnumerator<T>

再继续下一步之前,我们看一下IEnumerable接口和IEnumerator的定义

public interface IEnumerator
{
    bool MoveNext();
    Object Current {get; }
    void Reset();
}

public interface IEnumerable
{  
    IEnumerator GetEnumerator();
}
IEnumerator & IEnumerable

迭代器与列举有不一样的语法,在于迭代器需要返回可列举的接口或者列举器接口。

创建序列

迭代器可以进一步用于创建迭代。为了证实这点,我们可以扩展我们斐波纳契数列例子

static IEnumerable<int> Fibonacci(int number)
{ 
    for(int i=0, prevFib=1, curFib=1;i<number;i++)
    {
        yield return prevFib;
        int newFib = prevFib + curFib;
        prevFib = curFib;
        curFib = newFib;
    }
}

static IEnumerable<int> EvenNumbers(IEnumerable<int> sequence)
{
    foreach (int x in sequence)
        if (x % 2 == 0)
            yield return x;
}

static void Main(string[] args)
{
    foreach (int f in EvenNumbers(Fibonacci(8)))
        Console.WriteLine(f);

    Console.ReadLine();
}
Composable Iterator

请注意,直到Fibonacci方法所产生的数列的MoveNext()方法被调用时(执行foreach循环,会隐式地调用IEnumerator的MoveNext方法),才会判断该元素是否为偶数。

迭代器可以进一步用于创建迭代大量应用于LINQ。

转载于:https://www.cnblogs.com/yang_sy/p/3569541.html

18、深入理解 C 中的 foreach 语句与迭代器 本文深入解析了 C# 中的 `foreach` 语句与迭代器的工作原理,重点分析了状态机 `<Power>d__0` 的实现与状态转换过程。通过反编译 IL 代码,展示了 CLR 如何通过 `MoveNext` 方法控制迭代流程,并探讨了迭代器在性能、资源管理及实际应用中的关键点。文章还结合调试工具 `windbg.exe` 检查状态机的内存状态,帮助开发者更深入理解迭代机制。 阅读详情

相关推荐

C# 中的 yield return:5 种场景对比,让你的代码更简洁高效

通过今天的分享,相信大家已经对有了深入的理解。它不仅简化了代码,提高了可读性和可维护性,还在处理大型数据集和生成复杂序列时提供了显著的性能优势。希望这篇文章能帮助你在 C# 编程的道路上更进一步!如果你有任何问题或建议,欢迎在评论区留言交流。😊🌟。

java专栏 822

可迭代对象

可以对list、tuple、dict、set、str等类型的数据使用for...in...的循环语法从其中依次拿到数据进行使用,我们把这样的过程称为遍历,也叫迭代。 把可以通过for...in...这类语句迭代读取一条数据供我们使用的对象称之为可迭代对象(Iterable) 可迭代对象通过__iter__方法向我们提供一个迭代器,我们在迭代一个可迭代对象的时候,实际上就是先获取该对象提供的一个迭代器,然后通过这个迭代器来依次获取对象中的每一个数据。那么也就是说,一个具备了__iter__方法的...

小胡子 5852

C# Iterative迭代用法示例

C# Iterative迭代用法示例 

C# yield return 返回枚举器

1.举例   -------------类定义--------------     public class GameMoves     {         private int move = 0;         const int MaxMoves = 9;         private System.Collections.IEnumerator cross;

limlimlim的专栏 1405

从GAN生成失败案例出发:为什么你的AI画不准位置?CoordConv保姆级解决方案

本文深入探讨了GAN生成图像时位置偏移问题的根源,并提供了CoordConv的保姆级解决方案。通过分析传统CNN在空间坐标建模上的缺陷,详细介绍了CoordConv的原理、实现步骤及在StyleGAN2等模型中的实战应用,显著提升了医学图像生成等任务的定位精度。

pp12345的博客 204

基于STM32的家用新风系统智能控制器 | 保姆式教程 开源可复刻

本项目基于STM32F103C8T6核心控制器,完整实现了家用新风系统智能控制器的全功能开发,覆盖多传感器数据采集、双模式控制、PID调速、异常报警、掉电记忆等核心功能,全程采用保姆式无跳步教学,从硬件选型、接线、CubeMX配置、代码开发、调试排坑全流程拆解,跟着步骤一步步操作,就能顺利完成项目复刻

ZhongWeiXiang9的博客 466

通过c#反射初始化系统

在项目中,特别是ui系统,经常需要做一个ui系统的管理和ui系统初始化,以及ui系统销毁等情况。 一般有两种方式来管理ui系统 1.每加一个系统就在uimanager中加一个引用。 比如(伪代码) UIManager { uis.add(ASystemClass) } 类似这样做,然后用uis来统一管理ui 优点:这样写很直观,在一个ui系统做完,可以自己管理ui系统初始化顺序 ...

llsansun的博客 634

Synopsys PCIe IP 实战笔记:手把手教你配置本地数字回环(PIPE/RMMI)

本文详细介绍了Synopsys PCIe IP本地数字回环(PIPE/RMMI)的配置与调试全流程,涵盖硬件连接、寄存器操作及实战技巧。通过数字回环模式,开发者可在无真实对端设备的情况下验证PCIe接口功能,适用于芯片验证和FPGA原型开发。文章还提供了常见问题排查和性能优化建议,助力高效完成协议解析与调试。

weixin_30394669的博客 323

[C#.NET 拾遗补漏]07:迭代器和列举器

阅读本文大概需要 3 分钟。大家好,这是 [C#.NET 拾遗补漏] 系列的第 07 篇文章。在 C# 中,大多数方法都是通过 return 语句立即把程序的控制权交回给调用者,同时也会...

dotNET跨平台 244

N5105核显直通PVE:从黑屏到HDMI输出的完整指南

PCI直通能让虚拟机独占物理设备,但核显直通远没有独显那么轻松。Intel核显与CPU、显示输出链路的强耦合使UEFI引导、GOP驱动、驱动签名等环节都成了黑屏陷阱。在PVE虚拟化环境中,通过IOMMU开启、vfio-pci绑定、OVMF固件和Q35机型配合,可以绕过KVM检测,让Windows虚拟机正确驱动核显,从HDMI口输出画面,并解锁Intel Quick Sync硬解能力。这种方案尤其适合N5105等低功耗迷你主机,可作为客厅HTPC、本地视频硬解或依赖硬件加速的轻量工作台。本文基于N5105平台

weixin_31082403的博客 375

ethercat linux lan9252 - lan9253调试

linux 的ethercat测试

linsheng_111的专栏 1934

c# 差值 获取时间_[C#.NET 拾遗补漏]07:迭代器和列举器

大家好,这是 [C#.NET 拾遗补漏] 系列的第 07 篇文章。在 C# 中,大多数方法都是通过 return 语句立即把程序的控制权交回给调用者,同时也会把方法内的本地资源释放掉。而包含 yield 语句的方法则允许在依次返回多个值给调用者的期间保留本地资源,等所有值都返回结束时再释放掉本来资源,这些返回的值形成一组序列被调用者使用。在 C# 中,这种包含 yield 语句的方法、属性或索引器...

weixin_29051149的博客 84

电子电器架构 -- 汽车零部件DV试验与PV试验的定义及关键差异

电子电器架构 -- 汽车零部件DV试验与PV试验的定义及关键差异

Soly_kun的博客 733

.net 集合分成几个等数量集合_[C#.NET 拾遗补漏]07:迭代器和列举器

大家好,这是 [C#.NET 拾遗补漏] 系列的第 07 篇文章。在 C# 中,大多数方法都是通过 return 语句立即把程序的控制权交回给调用者,同时也会把方法内的本地资源释放掉。而包含 yield 语句的方法则允许在依次返回多个值给调用者的期间保留本地资源,等所有值都返回结束时再释放掉本来资源,这些返回的值形成一组序列被调用者使用。在 C# 中,这种包含 yield 语句的方法、属性或索引器...

weixin_39750854的博客 187

【目标检测】基于yolov5的道路坑洼检测(附代码和数据集)

基于yolov5的道路坑洼检测(附代码和数据集)

学无止境、积少成多、厚积薄发 4620

27、物联网中的量子密钥分发:安全通信新范式

本文探讨了物联网(IoT)快速发展所带来的安全挑战,并提出量子密钥分发(QKD)作为应对新兴威胁的新范式。文章详细介绍了QKD的基础原理、BB84协议的实现流程以及其在物联网中的应用前景,同时强调了安全专业人员提升技能、制定标准化指南以及跨学科合作的重要性。通过利用量子力学特性,QKD能够提供更高的通信安全性,抵御量子计算等未来威胁,为物联网的安全发展提供保障。

tequila的博客 157

C#学习之用迭代器实现枚举器

上一篇文章为自定义类实现了foreach的功能,但实现过程中要实现IEnumerator接口(包括MoveNext、Reset函数和Current属性)以便GetEnumerator函数能获取枚举数,有点复杂,有点繁琐。 已经知道:主要一个类实现了GetEnumerator函数就能foreach,而实现IEnumerator接口只是实现 GetEnumerator函数的其中一个办法,也就说还有其...

weixin_33758863的博客 208

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

C#迭代器

一、自定义迭代器 class Iterator { private int[] Nums; public Iterator(int[] nums) { Nums = nums; } public int index = -1; public bool MoveNext(...

weixin_41963220的博客 250

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Unity3d C#:从迭代器到协程

进程、线程、协程是几个很基础且重要的知识点。而在C#中要理解协程得先理解一下迭代器。接下来从一个初学者的角度浅要分析(如有误还恳请指出)。先简单说明一下迭代器。迭代器通过去继承和实现IEnumerable跟IEnumerator这两接口,接下来便可以为foreach所使用。整体可参考:C#迭代器 - 知乎接下来从应用的角度介绍几种能够实现迭代器的办法。这是最基础的方法,但是通过这种方法能很好滴能清楚背后的原理。该部分及方法二参考自:C# 迭代器_Go_Accepted的博客-优快云博客_c#迭代器 调

qq_42987967的博客 729

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

17/23 迭代器模式

迭代器模式(Iterator) 提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。 如果说在JAVA和C#代码编写过程中使用最多的设计模式不是工厂,它一定是迭代器模式。 在C#中集合、数组有以下类型 Array、List、HashTable、Stack、Queue、Set、BitArray等等… 而在C#中对于集合或数组的访问方式是 foreach(var item in List) { } foreach(var item in Stack) { } ## C#

XiaoCai 1394

C#的IEnumerable可枚举的集合

接口是 .NET 中集合类的基础接口之一,包括数组、列表、字典等常见的集合类都实现了该接口。它提供了一种标准的方式来遍历集合中的元素,使用迭代器模式实现了对集合的迭代访问。它提供了一种标准的方式来遍历集合中的元素,使用迭代器模式实现了对集合的迭代访问。这种方式可以提供一种统一的方法来处理不同类型的集合,并通过迭代器实现惰性加载,避免一次性加载整个集合的开销。这种方式可以提供一种统一的方法来处理不同类型的集合,并通过迭代器实现惰性加载,避免一次性加载整个集合的开销。接口的迭代器对象,用于遍历集合中的元素。

ultramand的博客,重生之我在cad上二次开发猛猛敲C#代码~ 2413

C# 设计模式:第十七章 - 行为型模式 之 迭代器模式(Iterator Pattern)

迭代器模式是一种行为型设计模式,它提供了一种方法来顺序访问一个聚合对象的各个元素,而不需要暴露该对象的内部表示。通过迭代器模式,你可以定义一个接口来遍历集合中的元素,而不需要关心集合的具体类型或结构。这使得代码更加灵活、可扩展,并且可以应用于各种不同的数据结构。除了简单的列表集合,我们还可以为更复杂的自定义集合实现迭代器模式。假设你正在开发一个双向链表(Doubly Linked List),并且希望为它实现一个迭代器,以便用户可以轻松地遍历链表中的节点。在这章中,我们学习了迭代器模式。

caifox的博客 1358

3-7 C#自定义集合和常用集合

解决办法:新建一个集合类,去存放从集合中获得的迭代器。重新放置一个数字,在主函数中也要放置两个迭代器,注意迭代器的位置。SortedList SortedDic..//常用于算法栈和队列。Dictionary<TKey,TValue>//查找效率极高。class MyList:IEnumerable<类型>集合只有一层循环由于只有一个迭代器,很容易出现bug。Linkedlist<T>//反着的list。集合List适合于集合数组,

m0_66701835的博客 225

C# yield关键字解析

相信好多程序员都是因为unity的协程(Coroutine)认识yield这个关键字的,知道在unity的开发中诸如yield return null、yield return new WaitForSeconds(1.0f)的用法,其实yield是C#的关键字,unity的协程只是在c#的基础上做了一层封装,我们现在来看看yield这个关键字。 说到yield就不得不说迭代器,迭代器模式是设计...

qq_35037137的博客 379

C#中的LINQ之美:优雅的数据查询与操作

LINQ(Language Integrated Query,语言集成查询)是C#中一个强大的工具,它将查询功能直接融入到语言中,使开发者能够以一种更直观、更接近自然语言的方式来操作数据。LINQ不仅能极大地提高开发效率,而且让代码变得更加简洁易读。

771
上一篇: C#记录对象的变化
下一篇: C#相等性比较
aoding8407
博客等级 码龄11年 2粉丝 0原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值