您的位置:首页 > 编程语言 > C#

也来谈谈C#中的foreach和yield

2020-02-03 03:10 921 查看

在C#中,foreach的使用简化了很多循环语法的书写。如果初学者仅仅把foreach当成for循环的省略写法的话,就显得有点大才小用了。事实上,foreach与“迭代”和“枚举”密切相关。

C#编译器会把foreach语句转换为IEnumerable接口的方法和属性。例如:

            foreach (Person p in persons)
            {
                Console.WriteLine(p);
            }

以上代码迭代persons数组中的所有元素,并逐个显示他们。

foreach语句会解析成下面的代码。首先调用GetEnumerator()方法,获得数组的一个枚举。在while循环中(只要MoveNext()返回true),用Current属性访问数组中的元素:

            IEnumerator enumerator = persons.GetEnumerator();
            while (enumerator.MoveNext())
            {
                Person p = (Person) enumerator.Current;
                Console.WriteLine(p);
            }

这里要说明的是。用[]声明数组是C#中使用Array类的记号。Array类实现了IEnumerable接口中的GetEnumerator()方法。使用foreach语句迭代数组,其实是使用了Array类中个GetEnumerator()方法。

也就是说,只要是实现了IEnumerable接口中的GetEnumerator()方法的类,都可以用foreach语句来迭代。

IEnumerable接口中的GetEnumerator()方法是这样定义的:

IEnumerator GetEnumerator()

其返回的类型是一个IEnumerator接口。

IEnumerator接口中的Current属性返回光标所在的元素。

IEnumerator接口中的MoveNext()方法移动到集合的下一个元素上,如果有这个元素,该方法就返回true。如果集合不再有更多的元素,该方法就返回false。

IEnumerator接口中的Reset()方法将光标重新定位于集合的开头。许多枚举会抛出NotSupportedException异常。

下面,我们来写一个实现了IEnumerable接口的类。

                    public class HelloCollection:IEnumerable
                    {
                        public IEnumerator GetEnumerator()
                        {
                            yield return "Hello";
                            yield return "World";
                        }
                    }

现在可以用foreach语句迭代了:

        static void Main(string[] args)
        {

            HelloCollection helloCollection=new HelloCollection ();
            foreach (string s in helloCollection )
            {
                Console.WriteLine(s);
            }
        }

实际上,yield return语句返回集合的一个元素,并移动到下一个元素上。它会将类HelloCollection解析成如下代码:

        public class HelloCollection : IEnumerable
        {
            public IEnumerator GetEnumerator()
            {
                Enumertor enumerator = new Enumerator();
                return enumerator;
            }

            public class Enumertor : IEnumerator, IDisposable
            {
                private int state;
                private object current;

                public Enumertor(int state)
                {
                    this.state = state;
                }
            }
                bool System.Collections .IEnumerator .MoveNext()
                {
                    switch (state)
                    {
                        case 0:
                            current = "Hello";
                            state = 1;
                            return true;
                        case 1:
                            current = "World";
                            state = 2;
                            return true ;
                        case 2:
                            break ;
                    }
                    return false ;
                }

                void System.Collections .IEnumerator .Reset()
                {
                    throw new NotSupportedException();
                }

                object System.Collections .IEnumerator .Current
                {
                    get { return current; }
                }
                void IDisposable.Dispose()
                {
                }
        }

 

foreach语句默认用GetEnumerator()方法迭代,也可以自行制定迭代方法。举例:

        public class MusicTitles:IEnumerable
        {
            string[] names = { "Tubular Bells", "Hergest Ridge", "Ommadawn", "Platinum" };

            public IEnumerator GetEnumerator()  /*顺序迭代*/
            {
                for (int i = 0; i < 4; i++)
                    yield return names[i];
            }

            public IEnumerator Reverse()  /*逆序迭代*/
            {
                for (int i = 3; i >= 0; i--)
                    yield return names[i];
            }
        }

在foreach语句中不必写明使用GetEnumerator()方法迭代,因为这是默认方法。如下:

        static void Main(string[] args)
        {

            MusicTitles titles = new MusicTitles();
            foreach (string title in titles)
            {
                Console.WriteLine(title);
            }

            Console.WriteLine();

            foreach (string title in titles.Reverse())
            {
                Console.WriteLine(title);
            }
        }
 

参考资料:《C#高级编程(第6版)》

转载于:https://my.oschina.net/huxuanhui/blog/33285

  • 点赞
  • 收藏
  • 分享
  • 文章举报
站内首发文章 chuxuzhui0592 发布了0 篇原创文章 · 获赞 0 · 访问量 674 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: