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. bootstrap easyUI 的选型

    最近开始搭建新的项目后台,开始在Bootstrap 和 EasyUI两个框架上选型,个人还是倾向于Bootstrap,毕竟这个响应式效果是个趋势,并且可以自动兼容不同的屏幕分辨率和设备.

  2. ubuntu14.04 swap not avalible交换分区不能使用

    系统最近特别卡,打开"System monitor"中的resource发现"swap not avalibe".原来系统每交换分区. 我的是笔记本电脑,存储空间有限.首先我下载磁盘分区工具Gpart ...

  3. debmirror镜像站

    如何建立一个Debian镜像网站呢?在Debian的官方网站已经有专门的介绍: http://www.debian.org/mirror/ftpmirror 这是基于rsync软件的方法,网页也提供了 ...

  4. WordPress文件上传与下载问题解决

    网上流传了一些修改WordPress文件上传大小限制的做法,大部分是一个版本,而且说得不够准确,特别是对于生手的指导性不强,本文总结了使用Wordpress博客的朋友在文件上传与下载时大小限制,及文件 ...

  5. Oracle学习.Windows 命令行 启动ORACLE服务与实例

    使用数据库前要先打开数据库的实例和监听器! --总结启动命令如下: lsnrctl  [start|stop|status]                  --启动监听器,停止监听器,查看监听器的 ...

  6. ios7新特性实践

    一 iOS 7 新特性:视图控制器切换API 二 iOS 7系列译文:认识 TextKit 三 iOS 7系列译文:iOS7的多任务处理 四 iOS7 最佳实践:一个天气应用案例(上) 五 iOS7 ...

  7. Introspector(内省)简单演示样例 与 简单应用

    简单演示样例: package com.asdfLeftHand.test; import java.beans.BeanDescriptor; import java.beans.BeanInfo; ...

  8. NVMe 与 AHCI

    http://elf8848.iteye.com/blog/1731274 AHCI: NCQ技术,600MB/S,一个队列,每个队列32个指令 NVME:65000个队列,每个队列65000指令,3 ...

  9. Choosing a Linux Tracer ------Brendan Gregg's Blog

    home Choosing a Linux Tracer (2015) 08 Jul 2015 Linux Tracing is Magic! A tracer is an advanced perf ...

  10. KDB调试内核

    http://www.ibm.com/developerworks/cn/linux/l-kdbug/