前天在项目中遇到一个问题,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 = -;
} public object Current
{
get { return strList[position]; }
} public bool MoveNext()
{
position ++;
if (position < strList.Length) return true;
return false;
} public void Reset()
{
position = -;
}
}
public class MyIEnumerable : IEnumerable
{
private string[] strList; public MyIEnumerable(string[] strList)
{
this.strList = strList;
}
public IEnumerator GetEnumerator()
{
return new MyIEnumerator(strList);
}
}

进行调用:

string[] strList = {"", "", "", ""};
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[]; public School()
{
Create();
} public void Create()
{
stu[] = new Student(, "tom");
stu[] = new Student(, "john");
stu[] = new Student(, "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你真的懂它吗?

Foreach遍历的更多相关文章

  1. 用<forEach>遍历list集合时,提示我找不到对象的属性

    <c:forEach items="${list}" var="item"> <tr> <td>${item.UserId} ...

  2. 使用yield关键字让自定义集合实现foreach遍历

    一般来说当我们创建自定义集合的时候为了让其能支持foreach遍历,就只能让其实现IEnumerable接口(可能还要实现IEnumerator接口) 但是我们也可以通过使用yield关键字构建的迭代 ...

  3. js中三个对数组操作的函数 indexOf()方法 filter筛选 forEach遍历 map遍历

     indexOf()方法  indexOf()方法返回在该数组中第一个找到的元素位置,如果它不存在则返回-1. 不使用indexOf时 var arr = ['apple','orange','pea ...

  4. mybatis map foreach遍历

    mybatis map foreach遍历 转至http://www.cnblogs.com/yg_zhang/p/4314602.html mybatis 遍历map实例 map 数据如下 Map& ...

  5. foreach遍历遇到的一个细节问题

    1.Invalid argument supplied for foreach()警告错误解决办法:foreach遍历之前添加is_array()判断

  6. IEnumerable 接口 实现foreach 遍历 实例

    额 为啥写着东西? 有次面试去,因为用到的时候特别少 所以没记住, 这个单词 怎么写! 经典的面试题: 能用foreach遍历访问的对象的要求? 答:  该类实现IEnumetable 接口   声明 ...

  7. 实现Foreach遍历

    实现Foreach遍历的集合类,需要实现IEnumerable接口,泛型集合则需要实现IEnumerable<T>接口 using System; using System.Collect ...

  8. foreach遍历扩展(二)

    一.前言 假设存在一个数组,其遍历模式是根据索引进行遍历的:又假设存在一个HashTable,其遍历模式是根据键值进行遍历的:无论哪种集合,如果它们的遍历没有一个共同的接口,那么在客户端进行调用的时候 ...

  9. c#--foreach遍历的用法与split的用法

    一. foreach循环用于列举出集合中所有的元素,foreach语句中的表达式由关键字in隔开的两个项组成.in右边的项是集合名,in左边的项是变量名,用来存放该集合中的每个元素.      该循环 ...

随机推荐

  1. 测试Oracle 11gr2 RAC 非归档模式下,offline drop数据文件后的数据库的停止与启动测试全过程

    测试Oracle 11gr2 RAC 非归档模式下,offline drop数据文件后的数据库的停止与启动测试全过程 最近系统出现问题,由于数据库产生的日志量太大无法开启归档模式,导致offline的 ...

  2. 环境变量/path/classpath/JAVA_HOME/JAVA环境变量配置

    环境变量 环境变量是在操作系统中一个具有特定名字的对象,它包含了一个或者多个应用程序所将使用到的信息.例如Windows和DOS操作系统中的path环境变量,当要求系统运行一个程序而没有告诉它程序所在 ...

  3. IBatis.Net使用总结(三)-- IBatis实现分页返回数据和总数

    IBatis 分页,这里没有使用其他插件,只使用最原始的方法. 输入参数: int currentPage 当前页 int  pageSize 每页大小 Hashtable findCondition ...

  4. IDF实验室:倒行逆施

    简单的PE文件逆向(.exe-IDA) 下载下来文件之后发现是一个exe文件,运行以后发现.

  5. hdu 5677 ztr loves substring 多重背包

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission( ...

  6. iOS ---不一样的NSLog打印(精准打印)

    在iOS开发过程中,调试是很重要的过程,而除了各种断点调试(普通断点.条件断点.全局断点)之外,似乎NSLog是我们调试最常用的方法,当然,也是最简单朴素的寻debug方法. 在项目中,我们常使用的N ...

  7. nodejs 提示‘xxx’ 不是内部或外部命令解决方法

    本文介绍了node.js包管理工具npm安装模块后,无法通过命令行执行命令,提示‘xxx’ 不是内部或外部命令的解决方法,给需要的小伙伴参考下.   一般出现这样的问题原因是npm安装出现了问题,全局 ...

  8. 弹性盒子布局flexbox

    弹性盒子display:flexbox一般应用于父元素的容器上,然后对子元素来进行弹性布局 设置了flexbox的父元素不能设置具体的宽度与高度的值,而是通过子元素来设置值,父元素弹性的包裹既可 相关 ...

  9. JavaScript函数表达式

    函数表达式的基本语法形式 var functionName = function(arguments){ //函数体 } 递归建议 我们通过例子来一步步说明,递归的最佳实现方式.下面是普通递归调用的例 ...

  10. maven ClassNotFoundException: org.springframework.web.context.ContextLoaderL

    信息: Starting Servlet Engine: Apache Tomcat/6.0.32 2012-3-31 9:39:40 org.apache.catalina.core.Standar ...