foreach 在编译成IL后,实际代码如下:

即:foreach实际上是先调用可枚举对象的GetEnumerator方法,得到一个Enumerator对象,然后对Enumerator进行while循环的相关操作,然后得到可枚举对象中的每一个值。

可以把可枚举对象中的所有值想像成一个链表,Enumerator是链表的指针,Enumerator.Current是当前指向的元素,Enumerator.MoveNext是指针后移。于是用while循环便可以用类似遍历链表的方式得到对象中的所有值。

一个可枚举对象,本身必需实现IEnumerable接口(其中只有一个GetEnumerator方法)。

在GetEnumerator方法中,可以直接返回一个Enumerator对象用于枚举。

也可以用多个yield return返回所有需要枚举的值,yield 语句在这里会创建一个实现了IEnumerator接口的对象。

要注意的是,如果在方法中用了yield return,就不能用普通的return,且如果用了yield return,那么方法体中的代码不会在调用时运行,只会在枚举开始后(调用Enumerator.MoveNext())才开始运行。且每一次枚举都只会运行到下一个yield return。

class test : IEnumerable
{
public static int j = ;
public static string ss = "begin"; public int i = ;
public IEnumerable<string> a()
{
test.ss += "1111111111@"; //string[] aaaa = { "1", "2", "3" }; //return aaaa.AsEnumerable(); test.j++;
yield return test.j.ToString();
test.ss += "1111111111@";
test.j++;
yield return test.j.ToString();
test.j++;
yield return test.j.ToString(); } public IEnumerator GetEnumerator()
{
test.ss += "222222222222222@"; string[] aaaa = { "", "", "" }; return aaaa.GetEnumerator(); //test.j++;
//yield return test.j.ToString();
//test.j++;
//yield return test.j.ToString();
//test.j++;
//yield return test.j.ToString();
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine(test.ss);
test t = new test(); Console.WriteLine(test.ss); var tb = t.GetEnumerator(); Console.WriteLine(test.ss);
var a = t.a(); Console.WriteLine(test.ss);
var b = a.GetEnumerator();
//var c = b.Current;
//b.MoveNext();
//Console.WriteLine(c);
//b.MoveNext();
Console.WriteLine(test.ss);
int i = ;
foreach (var s in t)
{
i++;
//test.j = i;
Console.WriteLine(test.j + " " + s);
} Console.WriteLine(test.ss); ReadLine();
}
}

如上面的代码,方法GetEnumerator中用的是普通的return,此时运行到

var tb = t.GetEnumerator();

输出是:begin222222222222222@

因为调用了t.GetEnumerator();自然会运行  test.ss += "222222222222222@"; 所以输出是  begin222222222222222@

但继续往下运行,无论是调用了test.a()还是test.a().GetEnumerator()   test.ss的值都没有再改变,也就是没有执行  test.ss += "1111111111@";  也就是此时方法test.a中的代码一直没得得到执行。

直到foreach开始后,即枚举开始(调用了MoveNext),才开始执行test.a方法体中的代码。

也就是说,包含yield return的方法,已经不是一个普通的方法,用普通的调用方式调用该方法,是不会执行里面的任何代码的,直到调用了Enumerator.MoveNext方法。
且每一次调用Enumerator.MoveNext方法,代码只会执行到下一个
yield return,并将该yield return返回的结果做为此次枚举的值。
												

c#yield,IEnumerable,IEnumerator的更多相关文章

  1. C# ~ 从 IEnumerable / IEnumerator 到 IEnumerable<T> / IEnumerator<T> 到 yield

    IEnumerable / IEnumerator 首先,IEnumerable / IEnumerator 接口定义如下: public interface IEnumerable /// 可枚举接 ...

  2. ICollection IEnumerable/IEnumerator IDictionaryEnumerator yield

    Enumerable和IEnumerator接口是.NET中非常重要的接口,二者区别: 1. IEnumerable是个声明式的接口,声明实现该接口的类就是“可迭代的enumerable”,但并没用说 ...

  3. IEnumerable, IEnumerator接口

    IEnumerable接口 // Exposes the enumerator, which supports a simple iteration over a non-generic collec ...

  4. IEnumerable & IEnumerator

    IEnumerable 只有一个方法:IEnumerator GetEnumerator(). INumerable 是集合应该实现的一个接口,这样,就能用 foreach 来遍历这个集合. IEnu ...

  5. 【Unity|C#】基础篇(20)——枚举器与迭代器(IEnumerable/IEnumerator)

    [学习资料] <C#图解教程>(第18章):https://www.cnblogs.com/moonache/p/7687551.html 电子书下载:https://pan.baidu. ...

  6. IEnumerable、IEnumerator与yield的学习

    我们知道数组对象可以使用foreach迭代进行遍历,同时我们发现类ArrayList和List也可以使用foreach进行迭代.如果我们自己编写的类也需要使用foreach进行迭代时该怎么办呢? IE ...

  7. 从yield关键字看IEnumerable和Collection的区别

    C#的yield关键字由来以久,如果我没有记错的话,应该是在C# 2.0中被引入的.相信大家此关键字的用法已经了然于胸,很多人也了解yield背后的“延迟赋值”机制.但是即使你知道这个机制,你也很容易 ...

  8. IEnumerable和IEnumerator 详解 (转)

    原文链接:http://blog.csdn.net/byondocean/article/details/6871881 参考链接:http://www.cnblogs.com/hsapphire/a ...

  9. IEnumerable和IEnumerator 详解

    初学C#的时候,老是被IEnumerable.IEnumerator.ICollection等这样的接口弄的糊里糊涂,我觉得有必要切底的弄清楚IEnumerable和IEnumerator的本质. 下 ...

随机推荐

  1. JavaScript---网络编程(7)-Dom模型(节点间的层次关系,节点的增、删、改)

    利用节点间的层次关系获取节点: 上一节讲了3中获取的方式: * ※※一.绝对获取,获取元素的3种方式:-Element * 1.getElementById(): 通过标签中的id属性值获来取该标签对 ...

  2. 深入设计模式(二)——单例模式(Singleton Pattern)

    一.单例模式介绍 单例模式(Singleton Pattern),保证一个类只有一个实例,并提供一个访问它的全局访问点.单例模式因为Singleton封装它的唯一实例,它就可以严格地控制客户怎样访问它 ...

  3. Opencl API解释(一)

    首先注明:我用的AMD的opencl,它有很多sample代码,结合代码来解释这些API Opencl 常用的API 汇总总结: 信息查询函数 1. cl_int clGetDeviceInfo(cl ...

  4. 字符编码笔记:ASCII,Unicode和UTF-8,附带 Little endian和Big endian的解释

    作者: 阮一峰 日期: 2007年10月28日 今天中午,我突然想搞清楚Unicode和UTF-8之间的关系,于是就开始在网上查资料. 结果,这个问题比我想象的复杂,从午饭后一直看到晚上9点,才算初步 ...

  5. latin1字符集在navicat下显示乱码(mysql)

    用navicat查看一个表的内容时显示如下

  6. hdoj 2087 剪花布条

    剪花布条 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  7. JAVA基础英语单词表(下)

    quantity                     / 'kwɔntiti /                    量,数量 query                             ...

  8. Web测试点 转

    一.输入框 1.字符型输入框: (1)字符型输入框:英文全角.英文半角.数字.空或者空格.特殊字符"~!@#¥%--&*?[]{}"特别要注意单引号和&符号.禁止直 ...

  9. 异常-JAVA

    #异常   ##1.异常处理概述   1.异常时程序在执行过程中所产生的问题.导致异常的原因的有很多种,包括:   1. 用户输入了无效数据   2. 找不到一个需要打开的文件   3. 在通讯过程中 ...

  10. PAT 1004. Counting Leaves (30)

    A family hierarchy is usually presented by a pedigree tree.  Your job is to count those family membe ...