IEnumerable和IEnumerator 详解

posted at 2017.5.17 20:51 by 风信子

我们先看IEnumerable和IEnumerator两个接口的语法定义。其实IEnumerable接口是非常的简单,只包含一个抽象的方法GetEnumerator(),它返回一个可用于循环访问集合的IEnumerator对象。IEnumerator对象有什么呢?它是一个真正的集合访问器,没有它,就不能使用foreach语句遍历集合或数组,因为只有IEnumerator对象才能访问集合中的项,假如连集合中的项都访问不了,那么进行集合的循环遍历是不可能的事情了。

那么让我们看看IEnumerator接口有定义了什么东西。看下图我们知道IEnumerator接口定义了一个Current属性,MoveNext和Reset两个方法,这是多么的简约。既然IEnumerator对象时一个访问器,那至少应该有一个Current属性,来获取当前集合中的项吧。MoveNext方法只是将游标的内部位置向前移动(就是移到一下个元素而已),要想进行循环遍历,不向前移动一下怎么行呢?

详细讲解:

说到IEnumerable总是会和IEnumerator、foreach联系在一起。

C# 支持关键字foreach,允许我们遍历任何数组类型的内容:

//遍历数组的项

int[] myArrayOfInts = {10,20,30,40};

foreach(int i in my myArrayOfInts)

{

    Console.WirteLine(i);

}

虽然看上去只有数组才可以使用这个结构,其实任何支持GetEnumerator()方法的类型都可以通过foreach结构进行运算。

所以,要想类也可以使用foreach遍历其中的项,那我们就要修改类型使之支持这些接口。可以手工实现每一个方法,不过这得花费不少功夫。虽然自己开发GetEnumerator()、MoveNext()、Current和Reset()也没有问题,但有一个更简单的办法。因为System.Array类型和其他许多类型(如List)已经实现了IEnumerable和IEnumerator接口,你可以简单委托请求到System.Array。如下所示:

[csharp] view plain copy
 
  1. namespace MyCarIEnumerator  
  2. {  
  3.     public class Garage:IEnumerable  
  4.     {  
  5.         Car[] carArray = new Car[4];  
  6.   
  7.         //启动时填充一些Car对象  
  8.         public Garage()  
  9.         {  
  10.             carArray[0] = new Car("Rusty", 30);  
  11.             carArray[1] = new Car("Clunker", 50);  
  12.             carArray[2] = new Car("Zippy", 30);  
  13.             carArray[3] = new Car("Fred", 45);  
  14.         }  
  15.         public IEnumerator GetEnumerator()  
  16.         {  
  17.             return this.carArray.GetEnumerator();  
  18.         }  
  19.     }  
  20. }  
  21. //修改Garage类型之后,就可以在C#foreach结构中安全使用该类型了。  
[csharp] view plain copy
 
  1. //除此之外,GetEnumerator()被定义为公开的,对象用户可以与IEnumerator类型交互:   
  2. namespace MyCarIEnumerator  
  3. {  
  4.     class Program  
  5.     {  
  6.         static void Main(string[] args)  
  7.         {  
  8.             Console.WriteLine("*********Fun with IEnumberable/IEnumerator************\n");  
  9.             Garage carLot = new Garage();  
  10.   
  11.             //交出集合中的每一Car对象吗  
  12.             foreach (Car c in carLot)  //之所以遍历carLot,是因为carLot.GetEnumerator()返回的项时Car类型,这个十分重要  
  13.             {  
  14.                 Console.WriteLine("{0} is going {1} MPH", c.CarName, c.CurrentSpeed);  
  15.             }  
  16.   
  17.             Console.WriteLine("GetEnumerator被定义为公开的,对象用户可以与IEnumerator类型交互,下面的结果与上面是一致的");  
  18.             //手动与IEnumerator协作  
  19.             IEnumerator i = carLot.GetEnumerator();  
  20.             while (i.MoveNext())  
  21.             {   
  22.                 Car myCar = (Car)i.Current;  
  23.                 Console.WriteLine("{0} is going {1} MPH", myCar.CarName, myCar.CurrentSpeed);  
  24.             }  
  25.             Console.ReadLine();  
  26.         }  
  27.     }  

IEnumerable<T>接口

实现了IEnmerable<T>接口的集合,是强类型的。它为子对象的迭代提供类型更加安全的方式。

[csharp] view plain copy
 
  1. public  class ListBoxTest:IEnumerable<String>  
  2.    {  
  3.        private string[] strings;  
  4.        private int ctr = 0;  
  5.       
  6.        #region IEnumerable<string> 成员  
  7.        //可枚举的类可以返回枚举  
  8.        public IEnumerator<string> GetEnumerator()  
  9.        {  
  10.            foreach (string s in strings)  
  11.            {  
  12.                yield return s;  
  13.            }  
  14.        }  
  15.  
  16.        #endregion  
  17.  
  18.        #region IEnumerable 成员  
  19.        //显式实现接口  
  20.        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()  
  21.        {  
  22.            return GetEnumerator();  
  23.        }  
  24.  
  25.        #endregion  
  26.   
  27.        //用字符串初始化列表框  
  28.        public ListBoxTest(params string[] initialStrings)  
  29.        {   
  30.            //为字符串分配内存空间  
  31.            strings = new String[8];  
  32.            //复制传递给构造方法的字符串  
  33.            foreach (string s in initialStrings)  
  34.            {  
  35.                strings[ctr++] = s;   
  36.            }  
  37.        }  
  38.   
  39.        //在列表框最后添加一个字符串  
  40.        public void Add(string theString)  
  41.        {   
  42.            strings[ctr] = theString;  
  43.            ctr++;  
  44.        }  
  45.   
  46.        //允许数组式的访问  
  47.        public string this[int index]  
  48.        {  
  49.            get {  
  50.                if (index < 0 || index >= strings.Length)  
  51.                {   
  52.                    //处理不良索引  
  53.                }  
  54.                return strings[index];  
  55.            }  
  56.            set {   
  57.                strings[index] = value;  
  58.            }  
  59.        }  
  60.   
  61.        //发布拥有的字符串数  
  62.        public int GetNumEntries()  
  63.        {  
  64.            return ctr;  
  65.        }  
  66.    }  
[csharp] view plain copy
 
  1. class Program  
  2.   {  
  3.       static void Main(string[] args)  
  4.       {  
  5.           //创建一个新的列表框并初始化  
  6.           ListBoxTest lbt = new ListBoxTest("Hello""World");  
  7.   
  8.           //添加新的字符串  
  9.           lbt.Add("Who");  
  10.           lbt.Add("Is");  
  11.           lbt.Add("Douglas");  
  12.           lbt.Add("Adams");  
  13.   
  14.           //测试访问  
  15.           string subst = "Universe";  
  16.           lbt[1] = subst;  
  17.   
  18.           //访问所有的字符串  
  19.           foreach (string s in lbt)  
  20.           {  
  21.               Console.WriteLine("Value:{0}", s);  
  22.           }  
  23.           Console.ReadKey();  
  24.       }  
  25.   }  


 综上所述,一个类型是否支持foreach遍历,必须满足下面条件:

方案1:让这个类实现IEnumerable接口

方案2:这个类有一个public的GetEnumerator的实例方法,并且返回类型中有public 的bool MoveNext()实例方法和public的Current实例属性。

Tags: , ,

IT技术

添加评论

  Country flag

biuquote
  • 评论
  • 在线预览
Loading