IEnumerable接口

一、示例

using System;
using System.Collections;

namespace test1
{
    class Program
    {
        static void Main(string[] args)
        {
            ///<summary>什么是IEnumerable?</summary>
            ///IEnumerable及IEnumerable的泛型版本IEnumerable<T>是一个接口,它只含有一个方法GetEnumerator。Enumerable这个静态类型含有很多扩展方法,其扩展的目标是IEnumerable<T>。
            ///实现了这个接口的类可以使用Foreach关键字进行迭代(迭代的意思是对于一个集合,可以逐一取出元素并遍历之)。实现这个接口必须实现方法GetEnumerator。

            ///<summary>如何实现一个继承IEnumerable的类型?</summary>
            ///实现一个继承IEnumerable的类型等同于实现方法GetEnumerator

            ///该类已经实现了IEnumerable
            Person[] peopleArray =
            {
                new Person("xu","shuai"),
                new Person("zhu","xiao"),
                new Person("hu","xiang"),
            };
            //People类实现了IEnumerable
            var peopleList = new People(peopleArray);
            //枚举时先访问MoveNext方法
            //如果返回真,则获得当前对象,返回假,就退出此次枚举
            foreach (Person p in peopleList)
            {
                Console.WriteLine(p.FirstName + " " + p.LastName);
            }
            Console.ReadKey();
        }
    }
    /// <summary>
    /// Person类
    /// </summary>
    public class Person
    {
        public Person(string fName,string lName)
        {
            FirstName = fName;
            LastName = lName;
        }
        public string FirstName;
        public string LastName;
    }
    /// <summary>
    /// People类就是Person类的集合
    /// 但我们不能用List<Person>或者Person[],因为他们都实现了IEnumerable
    /// 我们要自己实现一个IEnumerable
    /// 所以请将People类想象成List<Person>或者类似物
    /// </summary>
    public class People : IEnumerable
    {
        private readonly Person[] _people;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="pArray"></param>
        public People(Person[] pArray)
        {
            //构造一个Person的集合
            _people = new Person[pArray.Length];
            for (int i = 0; i < pArray.Length; i++)
            {
                _people[i] = pArray[i];
            }
        }
        //实现IEnumerable需要实现GetEnumerator方法
        //GetEnumerator方法需要一个IEnumerator类型的返回值
        public IEnumerator GetEnumerator()
        {
            return new PeopleEnumerator(_people);
        }
    }
    /// <summary>
    /// 因为我们不能实例化一个接口。我们必须再写一个类PeopleEnumerator,
    /// 它继承这个接口,实现这个接口所有的成员:Current属性,两个方法MoveNext和Reset。
    /// Enumerator代表一个类似箭头的东西,它指向这个集合当前迭代指向的成员
    /// IEnumerator接口类型对非泛型集合实现迭代
    /// </summary>
    public class PeopleEnumerator : IEnumerator
    {
        public Person[] People;

        //每次运行到MoveNext或Reset时,利用get方法自动更新当前位置指向的对象
        object IEnumerator.Current
        {
            get
            {
                try
                {
                    //当前位置的对象
                    return People[_position];
                }
                catch (IndexOutOfRangeException)
                {
                    throw new InvalidOperationException();
                }
            }
        }
        //当前位置
        private int _position = -1;

        public PeopleEnumerator(Person[] people)
        {
            People = people;
        }
        //当程序运行到foreach循环中的in时,就调用这个方法获得下一个person对象
        public bool MoveNext()
        {
            _position++;
            //返回一个布尔值,如果为真,则说明枚举没有结束。
            //如果为假,说明已经到集合的结尾,就结束此次枚举
            return (_position < People.Length);
        }
        public void Reset() => _position = -1;
    }
}

二、使用yield关键字实现方法GetEnumerator
2.1 第一种方式需要注意循环的次数,否则会报错
 

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

namespace test2
{
    class Program
    {
        static void Main(string[] args)
        {
            Person[] personArray =
            {
                new Person("xushuai"),
                new Person("huxiang"),
                new Person("zhuxiao")
            };
            People peoples = new People(personArray);
            var enumerator = peoples.GetEnumerator();
            for (int i = 0; i < 5; i++)
            {
                enumerator.MoveNext();
                if (enumerator.Current != null)
                {
                    var currentP = (Person)enumerator.Current;
                    Console.WriteLine("Current is {0}", currentP.Name);
                }
            }
            Console.ReadKey();
        }
    }
    /// <summary>
    /// Person类
    /// </summary>
    public class Person
    {
        public string Name { get; set; }
        public Person(string name)
        {
            Name = name;  
        }
    }
    /// <summary>
    /// Person的集合
    /// </summary>
    public class People : IEnumerable
    {
        private readonly Person[] _persons;

        public People(Person[] persons)
        {
            _persons = persons;
        }
        public IEnumerator GetEnumerator()
        {
            return _persons.GetEnumerator();
        }
    }
}


 

2.2 第二种方式,不回报错,不过输出结果最后会始终输出最后的一个值,Current会一直停留在集合的最后一个元素

/// <summary>
    /// Person的集合
    /// </summary>
    public class People : IEnumerable
    {
        private readonly Person[] _persons;

        public People(Person[] persons)
        {
            _persons = persons;
        }
        public IEnumerator GetEnumerator()
        {
            foreach (var item in _persons)
            {
                yield return item;
            }
        }
    }

2.3 yield 循环只循环了三次

 /// <summary>
    /// Person的集合
    /// </summary>
    public class People : IEnumerable
    {
        private readonly Person[] _persons;

        public People(Person[] persons)
        {
            _persons = persons;
        }
        public IEnumerator GetEnumerator()
        {
            foreach (var item in _persons)
            {
                Console.WriteLine("test!");
                yield return item;
            }
        }
    }

2.4  yield只返回值,不赋值

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

namespace test3
{
    class Program
    {
        static void Main(string[] args)
        {
            IEnumerable<Vector> vectors = GetVectors();

            //Begin to call GetVectors
            foreach (var vector in vectors)
            {
                vector.X = 4;
                vector.Y = 4;
            }

            //Before this iterate, there are 3 members in vectors, all with X and Y = 4
            foreach (var vector in vectors)
            {
                //But this iterate will change the value of X and Y BACK to 1/2/3
                Console.WriteLine(vector);
            }
            Console.ReadKey();
        }
        static IEnumerable<Vector> GetVectors()
        {
            yield return new Vector(1, 1);
            yield return new Vector(2, 3);
            yield return new Vector(3, 3);
        }
    }
    public class Vector
    {
        public double X { get; set; }
        public double Y { get; set; }
        public Vector(double x, double y)
        {
            this.X = x;
            this.Y = y;
        }

        public override string ToString()
        {
            return string.Format("X = {0}, Y = {1}", this.X, this.Y);
        }
    }
}

在迭代的过程中改变集合的状态

foreach迭代时不能直接更改集合成员的值,但如果集合成员是类或者结构,则可以更改其属性或字段的值。不能在为集合删除或者增加成员,这会出现运行时异常。For循环则可以。

IEnumerable<Vector> vectors = GetVectors().ToList();

猜你喜欢

转载自blog.csdn.net/Abel_01_xu/article/details/83578564