C# 迭代器的使用例子

一、使用数组做迭代,foreach (Person p in people.Persons),如下:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace IEnumerableTest
{
    class Program
    {
        static void Main(string[] args)
        {
            Person[] persons = new Person[4]
            {
                new Person("John","Smith", 18, true), 
                new Person("Kite", "Green", 16, false),
                new Person("Lei", "Li", 17, true),
                new Person("Meimei", "Han", 15, false)
            };
            People people = new People(persons);
            foreach (Person p in people.Persons)
            {
                Console.WriteLine(p.ToString());
            }
        }
    }

    public class People
    {
        private Person[] _persons;
        public Person[] Persons
        {
            get { return _persons; }
            set { _persons = value; }
        }

        public People(Person[] persons)
        {
            this.Persons = persons;
        }
    }

    public class Person
    {
        public Person(string firstName, string lastName, int age, bool gender)
        {
            this.FirstName = firstName;
            this.LastName = lastName;
            this.Age = age;
            this.Gender = gender;
        }

        private string _firstName;
        public string FirstName
        {
            get { return _firstName; }
            set { _firstName = value; }
        }

        private string _lastName;
        public string LastName
        {
            get { return _lastName; }
            set { _lastName = value; }
        }

        private int _age;
        public int Age
        {
            get { return _age; }
            set { _age = value; }
        }

        private bool _gender;
        public bool Gender
        {
            get { return _gender; }
            set { _gender = value; }
        }

        public override string ToString()
        {
            string gender = string.Empty;
            if (this.Gender)
                gender = "male";
            else
                gender = "female";
            return string.Format("{0} {1}, {2}, is {3} years old.", this.FirstName, this.LastName, gender, this.Age);
        }
    }
}
View Code

二、由于一些必要的原因,不希望在类的外面直接操作people.Persons,则对类People改造如下:

    public class People
    {
        private Person[] _persons;
        public People(Person[] persons)
        {
            this._persons = new Person[persons.Length];
            for (int i = 0; i < persons.Length; i++)
            {
                _persons[i] = persons[i];
            }
        }
    }
View Code

此时,对people._persons进行遍历是不可能的。可是,我们仍然有对people._persons进行遍历的需求。

怎么办?继续改造类People,让我们可以直接对类People进行遍历:foreach (Person p in people)

怎么改造?添加迭代器,或者说让类People成为迭代器。

三、添加迭代器

方法一:

1.让类People继承IEnumerable

2.添加方法

IEnumerator IEnumerable.GetEnumerator()
        {
            //return 一个类对象,这个类继承了接口IEnumerator;
        }

如下: 

    public class People : IEnumerable
    {
        private Person[] _persons;
        public People(Person[] persons)
        {
            this._persons = new Person[persons.Length];
            for (int i = 0; i < persons.Length; i++)
            {
                _persons[i] = persons[i];
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            //return 一个类对象,这个类继承了接口IEnumerator;
            PeopleEnum pe = new PeopleEnum(_persons);
            return (IEnumerator)pe;
        }
    }
View Code
    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();
                }
            }
        }
    }
View Code

方法二:

直接让类People直接继承 IEnumerator, IEnumerable也是可行的

public class People : IEnumerator, IEnumerable
    {
        private Person[] _persons;
        public People(Person[] persons)
        {
            this._persons = new Person[persons.Length];
            for (int i = 0; i < persons.Length; i++)
            {
                _persons[i] = persons[i];
            }
        }

        int position = -1;
        public bool MoveNext()
        {
            position++;
            return (position < _persons.Length);
        }

        public void Reset()
        {
            position = -1;
        }

        object IEnumerator.Current
        {
            get
            {
                return Current;
            }
        }

        public Person Current
        {
            get
            {
                try
                {
                    return _persons[position];
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            //return 一个类对象,这个类继承了接口IEnumerator;
            return (IEnumerator)this;
        }
    }
View Code

到了这里,我们就可以通过foreach (Person p in people)来遍历了。

 四、迭代器的编码简化

对方法二中类People进行改造,去掉继承IEnumerator,在GetEnumerator()中用for循环,并使用关键字yield

重新改造类People,如下,编译器将自动生成 IEnumerable 或IEnumerable<T> 接口的 CurrentMoveNext 和 Dispose 方法

    public class People : IEnumerable
    {
        private Person[] _persons;
        public People(Person[] persons)
        {
            this._persons = new Person[persons.Length];
            for (int i = 0; i < persons.Length; i++)
            {
                _persons[i] = persons[i];
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            for (int i = 0; i < _persons.Length; i++)
            { 
                yield return _persons[i];
            }
        }
    }
View Code