Foreach遍历
前天在项目中遇到一个问题,foreach遍历过程中修改responses中的对象,其中responses的类型:IEnumerable<Order>,代码如下:
foreach (Order item in responses) { if (string.IsNullOrEmpty(item.Creator)) item.Creator = item.Creator2; }
结果可想而知,response的对象并没有被改变。这是为什么?
弄清楚问题之前需要明白什么是foreach。foreach语句为数组或者对象集合的每一个元素重复一个嵌入语句组,foreach语句用于循环访问集合以获取所需信息,但不应更改集合信息以避免不可预知的负作用。(百度百科)Foreach可以循环访问集合以获取所需信息,为什么foreach不能更改集合信息,什么样的对象能够foreach?这就涉及到IEnumerable和IEnumerator.
IEnumerable和IEnumerator是两个用来实现枚举的接口,相互协助来完成一个具有枚举功能能使用foreach的集合。IEnumerable是一个声明性接口,一个集合对象要foreach,必须实现IEnumerable接口,也既是必须以某种方式返回IEnumerator object。IEnumerator是一个实现式接口,它定义了具体的实现方法。下面首先介绍其定义:
定义IEnumerator接口:
public interface IEnumerator { object Current{ get; } bool MoveNext(); void Reset(); }
定义IEnumerable接口:
public interface IEnumerable { IEnumerator GetEnumerator(); }
了解定义后,下面实例具体实现IEnumerable和IEnumerator:
public class MyIEnumerator : IEnumerator { private string[] strList; private int position; public MyIEnumerator(string[] strList) { this.strList = strList; position = -1; } public object Current { get { return strList[position]; } } public bool MoveNext() { position ++; if (position < strList.Length) return true; return false; } public void Reset() { position = -1; } }
public class MyIEnumerable : IEnumerable { private string[] strList; public MyIEnumerable(string[] strList) { this.strList = strList; } public IEnumerator GetEnumerator() { return new MyIEnumerator(strList); } }
进行调用:
string[] strList = {"1", "2", "4", "8"}; MyIEnumerable my = new MyIEnumerable(strList); var tt = my.GetEnumerator(); while (tt.MoveNext()) { Console.Write("{0} ", tt.Current); }
结果如下:
那么在项目中,如何自定义一个类实现foreach,继承IEnumerable即可,如下实例:
定义实体类:
public class Student { public int Id; public string Name; public Student(int id, string name) { Id = id; Name = name; } }
定义student的集合类School,继承IEnumerable集合:
public class School : IEnumerable { public Student[] stu = new Student[3]; public School() { Create(); } public void Create() { stu[0] = new Student(1, "tom"); stu[1] = new Student(2, "john"); stu[2] = new Student(3, "mali"); } public IEnumerator GetEnumerator() { return this.stu.GetEnumerator(); } }
使用foreach遍历school:
School school = new School(); foreach (Student item in school.stu) { Console.WriteLine("Id is {0} ,Name is {1}", item.Id, item.Name); } foreach (Student item in school) { Console.WriteLine("Id is {0} ,Name is {1}", item.Id, item.Name); }
结果如下:
此时一个概念“迭代器”应该进入大家的视野。
迭代器是一种对象,它能够用来遍历标准模板库容器的部分或者全部元素,每个迭代器对象代表容器中的确定地址。
迭代器使开发人员能够在类或者结构中支持foreach迭代,而不必整个实现IEnumerable和IEnumerator接口。只需要提供一个迭代器,即可遍历类中的数据结构。当编译器检测到迭代器时,将自动生成IEnumerable和IEnumerator接口中的相应方法。
另外一个概念yield:yield关键字向编译器指示它所在的方法是迭代器。编译器生成一个类来实现迭代器块中表示的行为。在迭代器块中,yield关键字与return关键字结合使用,向枚举器对象提供值。yield关键字也可与break结合使用,表示迭代结束。
引用:
IEnumerable 使用foreach 详解
IEnumerator和IEnumerable的关系
先说IEnumerable,我们每天用的foreach你真的懂它吗?
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构