zoukankan      html  css  js  c++  java
  • C#foreach原理

    本文主要记录我在学习C#中foreach遍历原理的心得体会。

    对集合中的要素进行遍历是所有编码中经常涉及到的操作,因此大部分编程语言都把此过程写进了语法中,比如C#中的foreach。经常会看到下面的遍历代码:

                var lstStr = new List<string> { "a", "b" };
                foreach (var str in lstStr)
                {
                    Console.WriteLine(str);
                }

    实际此代码的执行过程:

    复制代码
                var lstStr = new List<string> {"a", "b"};
                IEnumerator<string> enumeratorLst = lstStr.GetEnumerator();
                while (enumeratorLst.MoveNext())
                {
                    Console.WriteLine(enumeratorLst.Current);
                }
    复制代码

    会发现有GetEnumerator()方法和IEnumerator<string>类型,这就涉及到可枚举类型和枚举器的概念。

    为了方便理解,以下为非泛型示例:

    复制代码
        // 摘要:
        //     公开枚举器,该枚举器支持在非泛型集合上进行简单迭代。
        public interface IEnumerable
        {
            // 摘要:
            //     返回一个循环访问集合的枚举器。
            //
            // 返回结果:
            //     可用于循环访问集合的 System.Collections.IEnumerator 对象。
            IEnumerator GetEnumerator();
        }
    复制代码

    实现了此接口的类称为可枚举类型,是可以用foreach进行遍历的标志。

    方法GetEnumerator()的返回值是枚举器,可以理解为游标。

    复制代码
        // 摘要:
        //     支持对非泛型集合的简单迭代。
        public interface IEnumerator
        {
            // 摘要:
            //     获取集合中的当前元素。
            //
            // 返回结果:
            //     集合中的当前元素。
            //
            // 异常:
            //   System.InvalidOperationException:
            //     枚举数定位在该集合的第一个元素之前或最后一个元素之后。
            object Current { get; }
    
            // 摘要:
            //     将枚举数推进到集合的下一个元素。
            //
            // 返回结果:
            //     如果枚举数成功地推进到下一个元素,则为 true;如果枚举数越过集合的结尾,则为 false。
            //
            // 异常:
            //   System.InvalidOperationException:
            //     在创建了枚举数后集合被修改了。
            bool MoveNext();
            //
            // 摘要:
            //     将枚举数设置为其初始位置,该位置位于集合中第一个元素之前。
            //
            // 异常:
            //   System.InvalidOperationException:
            //     在创建了枚举数后集合被修改了。
            void Reset();
        }
    复制代码

    而生成一个枚举器的的工具就是迭代器,以下是自定义一个迭代器的示例(https://msdn.microsoft.com/en-us/library/system.collections.ienumerator.aspx):

    复制代码
    using System;
    using System.Collections;
    
    // Simple business object.
    public class Person
    {
        public Person(string fName, string lName)
        {
            this.firstName = fName;
            this.lastName = lName;
        }
    
        public string firstName;
        public string lastName;
    }
    
    // Collection of Person objects. This class
    // implements IEnumerable so that it can be used
    // with ForEach syntax.
    public class People : IEnumerable
    {
        private Person[] _people;
        public People(Person[] pArray)
        {
            _people = new Person[pArray.Length];
    
            for (int i = 0; i < pArray.Length; i++)
            {
                _people[i] = pArray[i];
            }
        }
    
    // Implementation for the GetEnumerator method.
        IEnumerator IEnumerable.GetEnumerator()
        {
           return (IEnumerator) GetEnumerator();
        }
    
        public PeopleEnum GetEnumerator()
        {
            return new PeopleEnum(_people);
        }
    }
    
    // When you implement IEnumerable, you must also implement IEnumerator.
    public class PeopleEnum : IEnumerator
    {
        public Person[] _people;
    
        // Enumerators are positioned before the first element
        // until the first MoveNext() call.
        int position = -1;
    
        public PeopleEnum(Person[] list)
        {
            _people = list;
        }
    
        public bool MoveNext()
        {
            position++;
            return (position < _people.Length);
        }
    
        public void Reset()
        {
            position = -1;
        }
    
        object IEnumerator.Current
        {
            get
            {
                return Current;
            }
        }
    
        public Person Current
        {
            get
            {
                try
                {
                    return _people[position];
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
        }
    }
    
    class App
    {
        static void Main()
        {
            Person[] peopleArray = new Person[3]
            {
                new Person("John", "Smith"),
                new Person("Jim", "Johnson"),
                new Person("Sue", "Rabon"),
            };
    
            People peopleList = new People(peopleArray);
            foreach (Person p in peopleList)
                Console.WriteLine(p.firstName + " " + p.lastName);
    
        }
    }
    
    /* This code produces output similar to the following:
     *
     * John Smith
     * Jim Johnson
     * Sue Rabon
     *
     */
    复制代码

    在有了yield这个关键字以后,我们可以通过这样的方式来创建枚举器:

    复制代码
    using System;
    using System.Collections;
    
    // Simple business object.
    public class Person
    {
        public Person(string fName, string lName)
        {
            this.firstName = fName;
            this.lastName = lName;
        }
    
        public string firstName;
        public string lastName;
    }
    
    // Collection of Person objects. This class
    // implements IEnumerable so that it can be used
    // with ForEach syntax.
    public class People : IEnumerable
    {
        private Person[] _people;
    
        public People(Person[] pArray)
        {
            _people = new Person[pArray.Length];
    
            for (int i = 0; i < pArray.Length; i++)
            {
                _people[i] = pArray[i];
            }
        }
    
        // Implementation for the GetEnumerator method.
        IEnumerator IEnumerable.GetEnumerator()
        {
            for (int i = 0; i < _people.Length; i++)
            {
                yield return _people[i];
            }
        }
    
    }
    
    
    class App
    {
        static void Main()
        {
            Person[] peopleArray = new Person[3]
            {
                new Person("John", "Smith"),
                new Person("Jim", "Johnson"),
                new Person("Sue", "Rabon"),
            };
    
            People peopleList = new People(peopleArray);
            foreach (Person p in peopleList)
                Console.WriteLine(p.firstName + " " + p.lastName);
        }
    }
    复制代码

    yield和return一起使用才有意义,这个关键字就是为迭代器服务的,所以有yield所在的方法是迭代器,作用是返回相同类型的值的一个序列,执行到yield return 这个语句之后,函数并不直接返回,而是保存此元素到序列中,继续执行,直到函数体结束或者yield break。

    但是生成的此迭代器的函数体并不会在直接调用时运行,只会在foreach中迭代时执行。如:

    复制代码
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Reflection;
    
    // Simple business object.
    public class Person
    {
        public Person(string fName, string lName)
        {
            this.firstName = fName;
            this.lastName = lName;
        }
    
        public string firstName;
        public string lastName;
    }
    
    // Collection of Person objects. This class
    // implements IEnumerable so that it can be used
    // with ForEach syntax.
    public class People 
    {
        private Person[] _people;
    
        public People(Person[] pArray)
        {
            _people = new Person[pArray.Length];
    
            for (int i = 0; i < pArray.Length; i++)
            {
                _people[i] = pArray[i];
            }
        }
    
        // Implementation for the GetEnumerator method.
        public IEnumerable GetMyEnumerator()
        {
            Console.WriteLine("a");
            for (int i = 0; i < _people.Length; i++)
            {
                Console.WriteLine(i.ToString());
                yield return _people[i];
            }
        }
    
    }
    
    
    class App
    {
        static void Main()
        {
            Person[] peopleArray = new Person[3]
            {
                new Person("John", "Smith"),
                new Person("Jim", "Johnson"),
                new Person("Sue", "Rabon"),
            };
    
            People peopleList = new People(peopleArray);
            IEnumerable myEnumerator = peopleList.GetMyEnumerator();
            Console.WriteLine("Start Iterate");
            foreach (Person p in myEnumerator)
            {
                Console.WriteLine(p.firstName + " " + p.lastName);
            }
    
            Console.ReadLine();
        }
    }
    复制代码

    结果:

      

    转载:https://www.cnblogs.com/alongdada/p/7598119.html

    好好学习,天天向上。
  • 相关阅读:
    程序的跨平台性:除了能够运行,还必须保证运行的结果。
    任务并发执行是一个宏观概念,微观上是串行的。
    Swing与AWT在事件模型处理上是一致的。
    我们在学习JDBC的时候会过度到J2EE。
    AWT是Java最早出现的图形界面,但很快就被Swing所取代。
    在生成一个窗体的时候,点击窗体的右上角关闭按钮激发窗体事件的方法:窗体Frame为事件源,WindowsListener接口调用Windowsclosing()。
    在java.util中有EventListener接口:所有事件监听者都要实现这个接口。
    事件模型指的是对象之间进行通信的设计模式。
    经验之谈:Swing的开发工作会非常的累,而且这项技术正在走向没落。避免从事有这种特征的工作。
    重点掌握集合的四种操作:增加、删除、遍历、排序。
  • 原文地址:https://www.cnblogs.com/Zhengxue/p/11280944.html
Copyright © 2011-2022 走看看