C# 遍历(迭代)完全指南:for、foreach、while 怎么选?foreach 底层原理、集合遍历与增删坑一篇讲透
C# 遍历迭代完全指南for、foreach、while 怎么选foreach 底层原理、集合遍历与增删坑一篇讲透遍历也叫迭代说白了就是把一堆数据挨个访问一遍。它是编程里最高频的操作之一数组、List、字典、字符串……几乎天天都在写。可你是否清楚for和foreach到底有什么区别、该怎么选foreach背后悄悄做了什么为什么遍历集合时不能直接增删元素本文系统梳理 C# 中各种遍历方式、可遍历类型、foreach的底层原理IEnumerable/IEnumerator、yield以及高频踩坑点适合初学者建立完整知识体系也适合有基础的同学复习回顾。一、什么是遍历遍历就是按照一定规则依次访问一组数据中的每一个元素。一次遍历通常包含三个要素从哪开始起点如第 0 个元素怎么走每次前进的规则如索引 1何时结束终止条件如到达集合末尾。C# 中常用的遍历方式有for、foreach、while、do-while以及集合提供的ForEach方法。下面这张总览图先建立整体认识简单记忆for/while基于索引或条件由你手动控制循环foreach基于枚举器IEnumerable专门用来从头到尾遍历集合最简洁。二、遍历数组数组是最基础的数据结构for和foreach都能遍历。int[]nums{10,20,30,40,50};// 方式一for通过索引访问需要自己管理索引 i// i 从 0 开始每次 1直到等于数组长度 Length 时结束for(inti0;inums.Length;i){Console.WriteLine($下标{i}值{nums[i]});}// 方式二foreach不需要关心索引和长度直接拿到每个元素foreach(intninnums){Console.WriteLine(n);}for的优势是可以灵活控制步长和方向比如倒序遍历、隔一个取一个// 倒序遍历从最后一个元素开始向前走到第 0 个for(intinums.Length-1;i0;i--){Console.WriteLine(nums[i]);}// 步长为 2只访问下标为偶数的元素for(inti0;inums.Length;i2){Console.WriteLine(nums[i]);}多维数组与交错数组// 二维数组矩阵用 GetLength(维度) 获取每一维的长度注意不是 Lengthint[,]matrix{{1,2,3},{4,5,6}};for(intr0;rmatrix.GetLength(0);r)// 行数{for(intc0;cmatrix.GetLength(1);c)// 列数{Console.Write(matrix[r,c] );}Console.WriteLine();}// 交错数组数组的数组每一维长度可以不同int[][]jagged{new[]{1,2},new[]{3,4,5}};foreach(int[]rowinjagged)// 外层遍历每一行行本身是一个数组{foreach(intvinrow)// 内层遍历该行的每个元素{Console.Write(v );}Console.WriteLine();}三、遍历常用集合除了数组实际开发中用得更多的是各种泛型集合它们都能用foreach直接遍历。3.1 ListTvarlistnewListstring{张三,李四,王五};// foreach 遍历foreach(stringnameinlist){Console.WriteLine(name);}// List 还提供了 ForEach 方法传入一个委托Lambda来处理每个元素list.ForEach(nameConsole.WriteLine(name));3.2 DictionaryTKey, TValue字典是键值对集合遍历时拿到的是KeyValuePairvardictnewDictionaryint,string{{1,一},{2,二},{3,三}};// 遍历键值对同时拿到 Key 和 Valueforeach(KeyValuePairint,stringkvindict){Console.WriteLine($键{kv.Key}值{kv.Value});}// 只遍历键foreach(intkeyindict.Keys){Console.WriteLine(key);}// 只遍历值foreach(stringvalueindict.Values){Console.WriteLine(value);}3.3 HashSetT、QueueT、StackT// HashSet元素不重复、无序varsetnewHashSetint{3,1,2,1};foreach(intxinset){Console.WriteLine(x);}// Queue 队列先进先出。注意foreach 只是查看不会把元素出队varqueuenewQueueint(new[]{1,2,3});foreach(intxinqueue){Console.WriteLine(x);}// Stack 栈后进先出。foreach 同样不会弹出元素varstacknewStackint(new[]{1,2,3});foreach(intxinstack){Console.WriteLine(x);}3.4 遍历字符串字符串本质是字符序列也可以foreachstringtextabc123;foreach(charchintext){Console.WriteLine(ch);// 依次输出 a b c 1 2 3}四、foreach 的本质IEnumerable 与 IEnumerator这是理解遍历的核心。为什么数组、List、字典、字符串都能用foreach因为它们都实现了IEnumerable可枚举接口这个接口承诺我能提供一个枚举器让你挨个访问我的元素。4.1 两个关键接口// IEnumerable可枚举对象负责提供一个枚举器publicinterfaceIEnumerableoutT:IEnumerable{IEnumeratorTGetEnumerator();// 返回一个枚举器}// IEnumerator枚举器负责真正地一步步移动、取出当前元素publicinterfaceIEnumeratoroutT:IDisposable,IEnumerator{TCurrent{get;}// 当前位置的元素}publicinterfaceIEnumerator{boolMoveNext();// 前进到下一个元素成功返回 true已到末尾返回 falsevoidReset();// 重置到起始位置实际很少用objectCurrent{get;}}可以这样理解二者关系IEnumerable是集合它能发给你一个游标枚举器IEnumerator是那个游标它有MoveNext()往后挪一格和Current当前格的内容。4.2 foreach 的等价展开我们写的foreach编译器会把它翻译成下面这样的代码简化版// 我们写的代码foreach(varitemincollection){Use(item);}// 编译器大致等价于using(varenumeratorcollection.GetEnumerator())// 1. 获取枚举器using 保证最后释放{while(enumerator.MoveNext())// 2. 不断往后移动{varitemenumerator.Current;// 3. 取出当前元素Use(item);// 4. 执行循环体}}// 5. 离开 using 时调用 Dispose 释放整个执行流程如下图这也解释了一个重要现象foreach只依赖GetEnumerator/MoveNext/Current根本不关心集合内部是数组、链表还是哈希表所以任何实现了IEnumerable的类型都能被foreach遍历。4.3 用 yield return 快速写迭代器如果想让自己的类型可被 foreach可以去实现IEnumerable但更简单的方式是用yield return——编译器会自动帮你生成枚举器// 返回 0 到 max 之间的所有偶数publicIEnumerableintGetEvenNumbers(intmax){for(inti0;imax;i2){// yield return返回一个元素然后方法在这里暂停// 下次调用 MoveNext 时再从这里继续往下执行yieldreturni;}}// 使用和遍历普通集合一模一样foreach(intninGetEvenNumbers(10)){Console.WriteLine(n);// 0 2 4 6 8 10}yield还有一个特点是延迟执行、按需生成IEnumerableintevensGetEvenNumbers(1000000);// 这一行执行后方法体其实还没有运行不会真的生成一百万个数字foreach(intninevens){Console.WriteLine(n);if(n10)break;// 只枚举到 10后面的数字根本不会被生成}五、遍历中的修改与删除高频踩坑5.1 foreach 中不能修改集合结构在foreach遍历期间对集合进行Add、Remove等改变结构的操作会抛出InvalidOperationException集合已修改枚举操作可能无法执行varlistnewListint{1,-2,3,-4,5};// 错误写法foreach 中删除元素运行时直接抛异常foreach(intxinlist){if(x0)list.Remove(x);// 不允许}原因枚举器内部记录了集合的版本号一旦集合结构变化、版本号对不上MoveNext()就会抛异常这是一种防止遍历错乱的保护机制。5.2 遍历中删除元素的正确方法varlistnewListint{1,-2,3,-4,5};// 方法一倒序 for RemoveAt。删除后后面的元素前移但因为是倒着走不会漏元素for(intilist.Count-1;i0;i--){if(list[i]0)list.RemoveAt(i);}// 方法二用 List 自带的 RemoveAll一行搞定最推荐用于 Listlist.RemoveAll(xx0);// 方法三先用 ToList() 复制一份快照遍历快照、删除原集合foreach(intxinlist.ToList()){if(x0)list.Remove(x);}注意正序for中直接RemoveAt会漏元素——删除后后面的元素整体前移而索引又i就会跳过紧挨着的下一个元素。这也是为什么推荐倒序删除。字典删除元素可用键集合快照的方式兼容性最好vardictnewDictionaryint,string{{1,a},{2,b}};// 先 ToList 复制键再遍历复制出来的键去删除foreach(intkeyindict.Keys.ToList()){if(key2)dict.Remove(key);}5.3 能不能修改元素本身要区分改集合结构和改元素内容varpeoplenewListPerson{newPerson{Name张三,Age18}};// 引用类型foreach 中修改元素的属性是允许的集合结构没变foreach(Personpinpeople){p.Age;// 可以// people.Add(new Person()); // 但不能新增/删除元素}// 值类型foreach 的迭代变量是只读副本不能给它赋值foreach(intninnew[]{1,2,3}){// n n 1; // 编译错误无法为 foreach 迭代变量赋值}六、遍历方式对比与选择遍历方式是否需要索引典型场景能否改变集合结构特点for是数组 / List需控制步长、方向或边遍历边改可以删除用倒序直观、性能好foreach否所有实现IEnumerable的集合不能最简洁、可读性好while否条件不固定、流读取、数据拉取手动管理最灵活do-while否循环体至少要执行一次手动管理先做后判断List.ForEach否对 List 每个元素执行一个委托不能函数式写法性能方面现代 .NET数组for和foreach性能基本一致。JIT 会把数组的foreach直接优化成索引访问没有额外开销ListTfor理论上略快但foreach用的是结构体枚举器、通常也会被内联日常开发差异可以忽略极致性能 / 大数量可考虑SpanT配合for减少接口调用和装箱泛型集合IEnumerableT遍历值类型不会装箱而老式非泛型集合如ArrayList遍历会有装箱开销新项目应优先使用泛型集合。选择建议只是依次处理集合元素、不需要索引 —— 优先foreach需要索引、要修改元素、控制步长或方向 —— 用for终止条件不固定如读流、轮询—— 用while。七、常见坑与注意事项foreach 中增删元素会抛InvalidOperationException删除用倒序for、RemoveAll或快照正序 for 删除漏元素删除导致元素前移、索引自增会跳过相邻元素改用倒序多维数组长度用错多维数组要用GetLength(0/1)Length返回的是元素总数对 null 使用 foreach集合为null时遍历会抛NullReferenceException必要时先判空IEnumerable 重复枚举LINQ 查询每次foreach都会重新执行必要时用ToList()固化结果误以为 foreach 能改迭代变量值类型迭代变量是只读副本改它没有意义也无法编译Queue/Stack 的 foreach 会移除元素吗不会枚举不改变集合出队/入栈要用对应方法非泛型集合的装箱开销优先使用ListT、IEnumerableT等泛型类型。八、小结遍历就是依次访问一组数据三要素是起点、前进规则、终止条件数组常用for可控步长/方向和foreach简洁多维数组用GetLength交错数组可嵌套遍历List、Dictionary、HashSet、Queue、Stack、string 都能用foreach字典遍历得到KeyValuePairforeach的本质是通过IEnumerable.GetEnumerator()拿到枚举器再循环调用MoveNext()、取Current最后Disposeyield return可以极简地实现迭代器且是延迟、按需执行foreach中不能增删集合元素删除元素推荐倒序for、RemoveAll或ToList快照遍历集合首选foreach需要索引和精细控制时用for条件不固定时用while。建议动手把本文每段代码敲一遍再试着用yield写一个自己的可遍历类型就能彻底把遍历这块知识掌握扎实。

相关新闻

后训练不等于微调:一文讲清大模型 Post-training、SFT、LoRA、DPO 和 RLHF

后训练不等于微调:一文讲清大模型 Post-training、SFT、LoRA、DPO 和 RLHF

摘要:预训练完成后,模型还远远谈不上“好用”。它可能会续写文本,却不一定听得懂指令;可能知道很多知识,却不会按照固定格式输出;甚至给出了正确答案,置信度也未必可信。把基础模型改造成聊天助…

2026/10/12 3:57:23 阅读更多 →
多隐层网络梯度消失与Xavier、He初始化的数理推导

多隐层网络梯度消失与Xavier、He初始化的数理推导

多隐层网络的训练困难,十次里有九次出在梯度在层与层之间传递时出了问题。最近帮一位朋友排查一个四层全连接网络,结构不复杂,数据也正常,但损失就是卡在某个值附近下不去。我当时第一反应不是调学习率,而是让他把每一…

2026/10/12 3:57:23 阅读更多 →
mahjong-helper实战指南:牌效分析、防守判断与记牌技巧详解

mahjong-helper实战指南:牌效分析、防守判断与记牌技巧详解

简介:mahjong-helper是一款面向雀魂、天凤玩家的日本麻将实时辅助工具,核心解决对局中的牌效判断、防守安全度与记牌需求。它能自动分析手牌,综合进张与打点给出推荐舍牌,并在有人立直或多副露时标注各牌危险度,同时记…

2026/10/12 3:57:23 阅读更多 →

最新新闻

【I2C的七宗罪】深度总结:从协议原理到硬件故障排查

【I2C的七宗罪】深度总结:从协议原理到硬件故障排查

前言:最近在网上淘到了一篇讲解IIC的文章,讲的是一个老工程师的多年IIC调试工作的总结,看了之后感觉覆盖了几乎所有的IIC调试过程可能遇到的问题,由于在CSDN平台上面没有发现类似的完整的文章,现就此重新加工编辑一下&…

2026/10/13 7:01:55 阅读更多 →
新品发布后多久才能被AI推荐?——以iPhone Duo为例

新品发布后多久才能被AI推荐?——以iPhone Duo为例

新品发布后多久才能被AI推荐?——以iPhone Duo为例【摘要】Top1推荐率为0、总提及率仅7.1%:基于4款主流大模型、140个有效回答、9个品牌实测数据,拆解新品从「被报道」到「被AI推荐」的上架时差,将AI上架拆分为3道门槛&#xff0c…

2026/10/13 7:01:55 阅读更多 →
OS_labs内核沙盒:从页表映射到进程切换的可调试x86-64实操指南

OS_labs内核沙盒:从页表映射到进程切换的可调试x86-64实操指南

简介:本资源是一套面向高校操作系统课程学习者的C语言实验项目合集,聚焦进程管理、内存调度、文件系统与系统调用等核心原理的动手实践,适用于具备基础C编程能力的本科生及系统编程初学者。压缩包共12个文件,含5个C源码文件&#…

2026/10/13 7:01:55 阅读更多 →
医疗器械包装运输验证:GB/T4857标准应用与方案设计

医疗器械包装运输验证:GB/T4857标准应用与方案设计

医疗器械包装运输验证这件事,我这两年做得比较多,说句实话,大部分产品出事都出在“纸箱看起来没事,开箱后产品坏了”或者“运输途中振动导致精密部件脱落”这种不起眼的环节上。国内做医疗器械注册,包装验证经常是发补…

2026/10/13 7:01:55 阅读更多 →
Python函数参数:从提示词到temperature掌控大模型输出

Python函数参数:从提示词到temperature掌控大模型输出

我们开始上课。第14课,前面几节课,我们已经把Python基础语法过了一遍,也成功让自己的电脑接上了大语言模型的API,能写出类似“你今年几岁”这种问题,让AI回答。但是有个问题一直困扰着零基础的小朋友:同一个…

2026/10/13 7:01:55 阅读更多 →
Java实现GB28181 RTP PS流解包与RTMP推流实战

Java实现GB28181 RTP PS流解包与RTMP推流实战

1. 项目背景与整体设计思路1.1 为什么要在Java里做RTP PS流解包推RTMP做过安防流媒体对接的朋友应该都有体会,GB28181协议在国内安防行业几乎是绕不开的存在。摄像头、NVR、平台之间信令走SIP,媒体流走RTP承载PS封装,这套组合拳已经跑了很多年…

2026/10/13 7:00:55 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →