C# IEnumerable 和 IEnumerator接口浅析

  温故而知新,可以为师矣,有空经常复习一下基础知识是有必要的,并且能加深理解和记忆。

  Foreach常用于循环访问集合,对实现IEnumerable的接口的容器进行遍历,IEnumerable和IEnumerator接口我有时候也有点迷糊,按官方的解释,IEnumerable是枚举器接口,IEnumerator是迭代器接口,从字面意思来看相差不大,逐一分析一下。

IEnumerable接口

public interface IEnumerable
{ 
        IEnumerator GetEnumerator();
}

  继承IEnumerable接口的类需实现暴露出来的GetEnumerator()方法,并返回一个IEnumerator接口对象,看来真正做事的是IEnumerator,F12看一下IEnumerator又有什么鬼东西。

IEnumerator接口

public interface IEnumerator
{ 
        object Current { get; }
        bool MoveNext(); 
        void Reset();
}

  IEnumerator接口有三个东东,一个属性Current,返回当前集合中的元素,方法MoveNext()移动到下一个,遍历不都是向后遍历的嘛,Reset(),字面意思重置,这个容易理解。做个假设:既然IEnumerable接口返回是IEnumerator接口迭代器来实现的,那么仅继承IEnumerator迭代器接口能不能实现一个自定义容器?

定义一个Phone

public class Phone 
{
        public string Name;
        public Phone(string name)
        {
            this.Name = name;
        }
}

  定义一个名为MyEnumerator迭代器,并现实它接口IEnumerator

public class MyEnumerator : IEnumerator
{
        Phone[] p;
        int idx = -1;
        public MyEnumerator(Phone[] t)
        {
            p = t;
        }
        public object Current
        {
            get
            {
                if (idx == -1)
                    return new IndexOutOfRangeException();
                return p[idx];
            }
        }

        public bool MoveNext()
        {
            idx++;
            return p.Length > idx;
        }

        public void Reset()
        {
            idx = -1;
        }
}
    class Program
    {
        static void Main(string[] args)
        {
       show("-----------IEnumerator------------"); Phone[] phones
= new Phone[] { new Phone("iPhone 7s"), new Phone("iPhone 6s"), new Phone("iPhone 5s") }; MyEnumerator enumerator = new MyEnumerator(phones); while (enumerator.MoveNext()) { Phone p = enumerator.Current as Phone; show(p.Name); } Console.ReadKey(); } static void show(string i) { Console.WriteLine(i); } }

结果显示:

  果然不出所料,真正做事情的是IEnumerator接口,即可循环访问自定义的一个容器,不过,初衷是想用Foreach来做循环访问、遍历的。那好,那就只能显示IEnumerable接口来做。稍稍改造一下Phone类:

   public class Phone : IEnumerable
    {
        public string Name ;
        public Phone(string name)
        {
            this.Name = name;
        }

        Phone[] p;
        public Phone(Phone[] t)
        {
            p = t;
        }
        public IEnumerator GetEnumerator()
        {
            return new MyEnumerator(p);
        }
    }
static void Main(string[] args)
        {
            show("-----------IEnumerator------------");
            Phone[] phones = new Phone[] { new Phone("iPhone 7s"), new Phone("iPhone 6s"), new Phone("iPhone 5s") };
            MyEnumerator enumerator = new MyEnumerator(phones);
            while (enumerator.MoveNext())
            {
                Phone p = enumerator.Current as Phone;
                show(p.Name);
            }

            show("-----------IEnumerable------------");
            Phone phoneList = new Phone(phones);
            foreach (Phone p in phoneList)
            {
                show(p.Name);
            }
           
            Console.ReadKey();
        }

结果显示:

  大功告成,再扩展成通用的容器PhonePackage,继承泛型IEnumerable<T>接口即可。

   public class PhonePackage<T> : IEnumerable<T> 
    {
        private List<T> dataList = null;

        public void Add(T t)
        {
            if (dataList == null)
                dataList = new List<T>();
            dataList.Add(t);
        }

        public IEnumerator<T> GetEnumerator()
        {
            foreach (T t in dataList)
            {
                yield return t;
            }
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            foreach (T t in dataList)
            {
                yield return t;
            }
        }
    }
static void Main(string[] args)
        {
            show("-----------IEnumerator------------");
            Phone[] phones = new Phone[] { new Phone("iPhone 7s"), new Phone("iPhone 6s"), new Phone("iPhone 5s") };
            MyEnumerator enumerator = new MyEnumerator(phones);
            while (enumerator.MoveNext())
            {
                Phone p = enumerator.Current as Phone;
                show(p.Name);
            }

            show("-----------IEnumerable------------");
            Phone phoneList = new Phone(phones);
            foreach (Phone p in phoneList)
            {
                show(p.Name);
            }
            show("-----------IEnumerable<T>------------");
            PhonePackage<Phone> phonePackage = new PhonePackage<Phone>();
            phonePackage.Add(new Phone("iPhone 7s"));
            phonePackage.Add(new Phone("iPhone 6s"));
            phonePackage.Add(new Phone("iPhone 5s"));
            foreach (Phone p in phonePackage)
            {
                show(p.Name);
            }
            Console.ReadKey();
        }
        static void show(string i)
        {
            Console.WriteLine(i);
        }

结果显示:

  IEnumerator迭代器接口挺啰嗦的,yield是简化了遍历的语法糖而已。

 

参考List<T>源码:http://www.projky.com/dotnet/4.5.1/System/Collections/Generic/List.cs.html

posted @ 2017-02-22 14:46  山治先生  阅读(396)  评论(0编辑  收藏