c#yield,IEnumerable,IEnumerator

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 = 0;
        public static string ss = "begin";

        public int i = 0;
        public IEnumerable<string> a()
        {
            test.ss += "[email protected]";

            //string[] aaaa = { "1", "2", "3" };

            //return aaaa.AsEnumerable();

            test.j++;
            yield return test.j.ToString();
            test.ss += "[email protected]";
            test.j++;
            yield return test.j.ToString();
            test.j++;
            yield return test.j.ToString();

        }

        public IEnumerator GetEnumerator()
        {
            test.ss += "[email protected]";

            string[] aaaa = { "1", "2", "3" };

            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 = 10;
            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();

输出是:[email protected]

因为调用了t.GetEnumerator();自然会运行  test.ss += "[email protected]"; 所以输出是  [email protected]

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

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

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

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

IEnumerable &amp; IEnumerator

IEnumerable 只有一个方法:IEnumerator GetEnumerator(). INumerable 是集合应该实现的一个接口,这样,就能用 foreach 来遍历这个集合. IEnumerator 有Current属性,MoveNext(), Reset()两个方法. 当 foreach 使用到一个 IEnumerable 的集合上的时候,遍历是这样开始的: 1. 调用 GetEnumerator() 得到一个 IEnumerator 的对象. 2. 调用 MoveNext()

IEnumerator/ IEnumerable/ yield return/ StartCoroutine 详解

IEnumerator/ IEnumerable public interface IEnumerable { IEnumerator GetEnumerator(); } public interface IEnumerator { bool MoveNext(); void Reset(); Object Current { get; } } 在两者的使用上,有下面几点需要注意 1.一个Collection要支持foreach方式的遍历,必须实现IEnumerable接口(亦即,必须以某种方

IEnumerable和IEnumerator 详解

http://blog.csdn.net/byondocean/article/details/6871881 初学C#的时候,老是被IEnumerable.IEnumerator.ICollection等这样的接口弄的糊里糊涂,我觉得有必要切底的弄清楚IEnumerable和IEnumerator的本质. 下面我们先看IEnumerable和IEnumerator两个接口的语法定义.其实IEnumerable接口是非常的简单,只包含一个抽象的方法GetEnumerator(),它返回一个可用于

C# IEnumerable和IEnumerator的区别,如何实现

IEnumerable接口和IEnumerator接口是.NET中非常重要的接口,二者有何区别? 1. 简单来说IEnumerable是一个声明式的接口,声明实现该接口的类就是"可迭代的enumerable",但并没用说明如何实现迭代器(iterator).其代码实现为: public interface IEnumerable         {                IEnumerator GetEnumerator();          }    2. 而IEnumer

C# IEnumerator与 IEnumerable

1. 接口的使用 (1)  首先定义接口 public interface IBattleMapManager : { Stages CurrentStage { get; } event EventHandler<BeginFightEventArgs> EnterFight; } (2) 用定义实现类- 实现接口 public class BattleMapManager : IBattleMapManager, IDisposable { public Stages CurrentSta

转载 IEnumerable和IEnumerator 详解

初学C#的时候,老是被IEnumerable.IEnumerator.ICollection等这样的接口弄的糊里糊涂,我觉得有必要切底的弄清楚IEnumerable和IEnumerator的本质. 下面我们先看IEnumerable和IEnumerator两个接口的语法定义.其实IEnumerable接口是非常的简单,只包含一个抽象的 方法GetEnumerator(),它返回一个可用于循环访问集合的IEnumerator对象.IEnumerator对象有什么呢?它是一个真正的 集合访问器,没有

&lt;转载&gt;C#IEnumerable和IEnumerator 详解

初学C#的时候,老是被IEnumerable.IEnumerator.ICollection等这样的接口弄的糊里糊涂,我觉得有必要切底的弄清楚IEnumerable和IEnumerator的本质. 下面我们先看IEnumerable和IEnumerator两个接口的语法定义.其实IEnumerable接口是非常的简单,只包含一个抽象的方法GetEnumerator(),它返回一个可用于循环访问集合的IEnumerator对象.IEnumerator对象有什么呢?它是一个真正的集合访问器,没有它,

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

C#的yield关键字由来以久,如果我没有记错的话,应该是在C# 2.0中被引入的.相信大家此关键字的用法已经了然于胸,很多人也了解yield背后的“延迟赋值”机制.但是即使你知道这个机制,你也很容易在不经意间掉入它制造的陷阱. 目录 一.一个很简单的例子 二.简单谈谈“延迟赋值” 三.从反射的代码帮助我们更加直接的了解yield导致的延迟赋值 四.如果需要“立即赋值”怎么办? 后记 一.一个很简单的例子 下面是一个很简单的例子:Vector为自定义表示二维向量的类型,Program的静态方法G

IEnumerator &amp; IEnumerable

[IEnumerator] 用于遍历一个对象,IEnumerator在System.Collections命名空间中. public interface IEnumerator { object Current { get; } bool MoveNext (); void Reset (); } [IEnumerable] 用于遍历一个对象是否能被遍历,IEnumerable在System.Collections命名空间中. public interface IEnumerable { IEn