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)

    非常感谢您的耐心观看,您的关注是我最大的动力! 不积跬步无以至千里,不积小流无以成江海!
  • 相关阅读:
    模板方法设计模式(未完待续)
    适配器模式
    g2o:一种图优化的C++框架
    U14.04 teamviewer install
    小教训
    卡2-SLAM
    Odometry的发布和发布odom到base_link的tf变换
    #pragma once
    友元
    Ubuntu 14.04 安装配置强大的星际译王(stardict)词典
  • 原文地址:https://www.cnblogs.com/sheldon-blog/p/8093252.html
Copyright © 2011-2022 走看看