首页 > 代码库 > c#yield,IEnumerable,IEnumerator

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 += "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 = { "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();

输出是: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