zoukankan      html  css  js  c++  java
  • C#基础知识 yield与foreach

    什么时候可以使用yield的关键字来定义迭代器?

    • 迭代器的返回类型必须是IEnumerable、IEnumerable<T>、IEnumerator 或 IEnumerator<T>
    • 迭代器的入参不能包括ref或out类型的参数

    首先,我们定义一个简单的遍历。

            static void Main(string[] args)
            {
                List<Person> persons = new List<Person>();
                persons.Add(new Person { SaySome = "Hello World,I am Sheldon" });
                persons.Add(new Person { SaySome = "Hello World,I am Penny" });
                persons.Add(new Person { SaySome = "Hello World,I am Amy" });
                foreach (var item in persons)
                {
                    Console.WriteLine(item.SaySome);
                }
            }

    它的输出结果:

    接下来,我们使用yield,来实现同样的效果。

            static void Main(string[] args)
            {
                foreach (var item in GetEnumerator())
                {
                    Console.WriteLine(item.SaySome);
                }
            } 
    
            public static IEnumerable<Person> GetEnumerator()
            {
                yield return new Person { SaySome = "Hello World,I am Sheldon" };
                yield return new Person { SaySome = "Hello World,I am Penny" };
                yield return new Person { SaySome = "Hello World,I am Amy" };
            }

    虽然,我们得到了同样的结果,但是yield到底是如何做到的?

    (又是翻书,又是看msdn,终于得到了答案)

    使用yield语句时,它会自动生成一个枚举器,而不是仅仅生成一个包含项的列表。

    ——————————————————    以上内容关于yield描述了大概    ———————————————————————

    ——————————————————以下内容主要说明foreach如何迭代———————————————————————

    这个枚举器通过foreach调用,foreach中依次访问每一项时,就会访问这个枚举器,从而达到迭代大量数据,而无须一次把所有的数据写到内存中。

    关于枚举器,我查看了system.collection.generic空间下的源码。

    为了知道foreach中是如何通过枚举器来工作的。

    我们来根据上面的List集合声明一个简单的枚举器

    (这个枚举器,只是为了简单的说明一下问题)

    public class GameMoves
        {
            private IEnumerator cross;
            private IEnumerator circle;
            public GameMoves()
            {
                cross = Cross();
                circle = Circle();
            }
            private int move = 0;
            const int MaxMoves = 9;
            public IEnumerator Cross()
            {
                while (true)
                {
                    Console.WriteLine("Cross, move {0}", move);
                    if (++move >= MaxMoves)
                    {
                        yield break;
                    }
                    yield return circle;
                }
            }
            public IEnumerator Circle()
            {
                while (true)
                {
                    Console.WriteLine("Circle,move{0}",move);
                    if (++move>=MaxMoves)
                    {
                        yield break;
                    }
                    yield return cross;
                }
            }
        }
    View Code

    重写一下Main方法

                var game = new GameMoves();
                //将枚举器设置为由game.Cross()返回的枚举器类型
                IEnumerator enumerator = game.Cross();
                //第一次调用 MoveNext()时,会调用Cross()方法,Cross()方法使用yield返回另一个枚举器
                while (enumerator.MoveNext())
                {
                    //返回的值可以用Current属性访问,并设置为enumerator变量,用于下一次循环
                    enumerator = enumerator.Current as IEnumerator;
                }
    View Code

    通过上面的例子我们能看出使用while来变向说明foreach的内部执行方式。

    通过 foreach 语句或 LINQ 查询来使用迭代器方法。

    foreach 循环的每次迭代都会调用迭代器方法。 迭代器方法运行到 yield return 语句时,会返回一个 expression,并保留当前在代码中的位置。 下次调用迭代器函数时,将从该位置重新开始执行。

    (部分源自msdn)

    非常感谢您的耐心观看,您的关注是我最大的动力! 不积跬步无以至千里,不积小流无以成江海!
  • 相关阅读:
    668. Kth Smallest Number in Multiplication Table
    658. Find K Closest Elements
    483. Smallest Good Base
    475. Heaters
    454. 4Sum II
    441. Arranging Coins
    436. Find Right Interval
    410. Split Array Largest Sum
    392. Is Subsequence
    378. Kth Smallest Element in a Sorted Matrix
  • 原文地址:https://www.cnblogs.com/sheldon-blog/p/8093252.html
Copyright © 2011-2022 走看看