原文链接:http://blog.csdn.net/byondocean/article/details/6871881

参考链接:http://www.cnblogs.com/hsapphire/archive/2010/04/16/1713211.html

初学C#的时候,老是被IEnumerable、IEnumerator、ICollection等这样的接口弄的糊里糊涂,我觉得有必要切底的弄清楚IEnumerable和IEnumerator的本质。

下面我们先看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结构进行运算。

  1. public class Garage
  2. {
  3. Car[] carArray = new Car[4];  //在Garage中定义一个Car类型的数组carArray,其实carArray在这里的本质是一个数组字段
  4. //启动时填充一些Car对象
  5. public Garage()
  6. {
  7. //为数组字段赋值
  8. carArray[0] = new Car("Rusty", 30);
  9. carArray[1] = new Car("Clunker", 50);
  10. carArray[2] = new Car("Zippy", 30);
  11. carArray[3] = new Car("Fred", 45);
  12. }
  13. }

理想情况下,与数据值数组一样,使用foreach构造迭代Garage对象中的每一个子项比较方便:

  1. //这看起来好像是可行的
  2. lass Program
  3. {
  4. static void Main(string[] args)
  5. {
  6. Console.WriteLine("*********Fun with IEnumberable/IEnumerator************\n");
  7. Garage carLot = new Garage();
  8. //交出集合中的每一Car对象吗
  9. foreach (Car c in carLot)
  10. {
  11. Console.WriteLine("{0} is going {1} MPH", c.CarName, c.CurrentSpeed);
  12. }
  13. Console.ReadLine();
  14. }
  15. }

让人沮丧的是,编译器通知我们Garage类没有实现名为GetEnumerator()的方法(显然用foreach遍历Garage对象是 不可能的事情,因为Garage类没有实现GetEnumerator()方法,Garage对象就不可能返回一个IEnumerator对象,没有 IEnumerator对象,就不可能调用方法MoveNext(),调用不了MoveNext,就不可能循环的了)。这个方法是有隐藏在 System.collections命名空间中的IEnumerable接口定义的。(特别注意,其实我们循环遍历的都是对象而不是类,只是这个对象是一个集合对象

支持这种行为的类或结构实际上是宣告它们向调用者公开所包含的子项:

//这个接口告知调方对象的子项可以枚举

public interface IEnumerable

{

IEnumerator GetEnumerator();

}

可以看到,GetEnumerator方法返回对另一个接口System.Collections.IEnumerator的引用。这个接口提供了基础设施,调用方可以用来移动IEnumerable兼容容器包含的内部对象。

//这个接口允许调用方获取一个容器的子项

public interface IEnumerator

{

bool MoveNext();             //将游标的内部位置向前移动

object Current{get;}       //获取当前的项(只读属性)

void Reset();                 //将游标重置到第一个成员前面

}

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

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

下面我们来看看手工实现IEnumberable接口和IEnumerator接口中的方法:

  1. namespace ForeachTestCase
  2. {
  3. //继承IEnumerable接口,其实也可以不继承这个接口,只要类里面含有返回IEnumberator引用的GetEnumerator()方法即可
  4. class ForeachTest:IEnumerable     {
  5. private string[] elements;  //装载字符串的数组
  6. private int ctr = 0;  //数组的下标计数器
  7. /// <summary>
  8. /// 初始化的字符串
  9. /// </summary>
  10. /// <param name="initialStrings"></param>
  11. ForeachTest(params string[] initialStrings)
  12. {
  13. //为字符串分配内存空间
  14. elements = new String[8];
  15. //复制传递给构造方法的字符串
  16. foreach (string s in initialStrings)
  17. {
  18. elements[ctr++] = s;
  19. }
  20. }
  21. /// <summary>
  22. ///  构造函数
  23. /// </summary>
  24. /// <param name="source">初始化的字符串</param>
  25. /// <param name="delimiters">分隔符,可以是一个或多个字符分隔</param>
  26. ForeachTest(string initialStrings, char[] delimiters)
  27. {
  28. elements = initialStrings.Split(delimiters);
  29. }
  30. //实现接口中得方法
  31. public IEnumerator GetEnumerator()
  32. {
  33. return  new ForeachTestEnumerator(this);
  34. }
  35. private class ForeachTestEnumerator : IEnumerator
  36. {
  37. private int position = -1;
  38. private ForeachTest t;
  39. public ForeachTestEnumerator(ForeachTest t)
  40. {
  41. this.t = t;
  42. }
  43. #region 实现接口
  44. public object Current
  45. {
  46. get
  47. {
  48. return t.elements[position];
  49. }
  50. }
  51. public bool MoveNext()
  52. {
  53. if (position < t.elements.Length - 1)
  54. {
  55. position++;
  56. return true;
  57. }
  58. else
  59. {
  60. return false;
  61. }
  62. }
  63. public void Reset()
  64. {
  65. position = -1;
  66. }
  67. #endregion
  68. }
  69. static void Main(string[] args)
  70. {
  71. // ForeachTest f = new ForeachTest("This is a sample sentence.", new char[] { ' ', '-' });
  72. ForeachTest f = new ForeachTest("This", "is", "a", "sample", "sentence.");
  73. foreach (string item in f)
  74. {
  75. System.Console.WriteLine(item);
  76. }
  77. Console.ReadKey();
  78. }
  79. }
  80. }

IEnumerable<T>接口

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

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

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

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

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

IEnumerable和IEnumerator 详解 (转)的更多相关文章

  1. IEnumerable和IEnumerator 详解 分类: C# 2014-12-05 11:47 18人阅读 评论(0) 收藏

    原:<div class="article_title"> <span class="ico ico_type_Original">&l ...

  2. IEnumerable和IEnumerator 详解

    初学C#的时候,老是被IEnumerable.IEnumerator.ICollection等这样的接口弄的糊里糊涂,我觉得有必要切底的弄清楚IEnumerable和IEnumerator的本质. 下 ...

  3. IEnumerable和IEnumerator 详解 【转】

    初学C#的时候,老是被IEnumerable.IEnumerator.ICollection等这样的接口弄的糊里糊涂,我觉得有必要切底的弄清楚IEnumerable和IEnumerator的本质. 下 ...

  4. 转载 IEnumerable和IEnumerator 详解

    初学C#的时候,老是被IEnumerable.IEnumerator.ICollection等这样的接口弄的糊里糊涂,我觉得有必要切底的弄清楚IEnumerable和IEnumerator的本质. 下 ...

  5. IEnumerable和IEnumerator详解

    引言 IEnumerable是可枚举的所有非泛型集合的基接口, IEnumerable包含一个方法GetEnumerator(),该方法返回一个IEnumerator:IEnumerator提供通过C ...

  6. IEnumerable 使用foreach 详解

    自己实现迭代器 yield的使用 怎样高性能的随机取IEnumerable中的值 我们先思考几个问题: 为什么在foreach中不能修改item的值? 要实现foreach需要满足什么条件? 为什么L ...

  7. C# ~ 从 IEnumerable / IEnumerator 到 IEnumerable<T> / IEnumerator<T> 到 yield

    IEnumerable / IEnumerator 首先,IEnumerable / IEnumerator 接口定义如下: public interface IEnumerable /// 可枚举接 ...

  8. IEnumerator和IEnumerable详解

    IEnumerator和IEnumerable 从名字常来看,IEnumerator是枚举器的意思,IEnumerable是可枚举的意思. 了解了两个接口代表的含义后,接着看源码: IEnumerat ...

  9. IEnumerable<T> 接口和GetEnumerator 详解

    IEnumerable<T> 接口 .NET Framework 4.6 and 4.5   公开枚举数,该枚举数支持在指定类型的集合上进行简单迭代. 若要浏览此类型的.NET Frame ...

随机推荐

  1. Engine中如何更改lyr文件数据源的相对路径

    以下是使用relativebase存储layerfile的代码,如果需要修改数据源只要修改ILayer即可: string folderpath = @E:相对路径; string layerfile ...

  2. 去掉 Android工程中让人很不爽的“黄色警告”

    一:问题       二:解决方法 (1)选择android工程,右键Android Tools —> Clear Lint Markers 这种方式能够清除android工程里面的所有警告信息 ...

  3. 安卓开发_慕课网_ViewPager与FragmentPagerAdapter实现Tab实现Tab(App主界面)

    学习内容来自“慕课网” ViewPager与FragmentPagerAdapter实现Tab 将这两种实现Tab的方法结合起来.效果就是可以拖动内容区域来改变相应的功能图标亮暗 思路: Fragme ...

  4. Python数据结构与算法--算法分析

    在计算机科学中,算法分析(Analysis of algorithm)是分析执行一个给定算法需要消耗的计算资源数量(例如计算时间,存储器使用等)的过程.算法的效率或复杂度在理论上表示为一个函数.其定义 ...

  5. 初始block,关于定义的几个小题目

    block的定义和C语言指针函数非常相似,就可以照着指针函数的方法去依葫芦画瓢就可以了 block中的^只是用来表示这是一个block对象,和函数指针中的*作用一样,只是一个标识符 下面有三个小例子来 ...

  6. IOS MenuController初步了解

    IOS MenuController初步了解 默认情况下有以下控件已经支持MenuController. UITextField UITextView UIWebView 让其他控件也支持MenuCo ...

  7. 彻底退出所有的Acticity

    有时候点击回退键退出应用,会出现有些Activity不能完全退出的情况,那么可以使用前面这个方法: 在需要退出的Activity的onCreate()方法中加入 ExitApplication.get ...

  8. C语言-04-函数

    函数 函数是一组一起执行任务的语句,函数是一个可执行C程序必不可少的条件(至少一个main()函数),函数的定义形式 returnType functionName() { bodyOf of the ...

  9. 转 自定义View之onMeasure()

    可以说重载onMeasure(),onLayout(),onDraw()三个函数构建了自定义View的外观形象.再加上onTouchEvent()等重载视图的行为,可以构建任何我们需要的可感知到的自定 ...

  10. 菜鸟教程 Python100例 之实例29

    学习编程的路,走得好艰辛... 为了巩固基础知识,把菜鸟教程网上的实例拿来练习.. 在做到实例29时,看了网站给出的代码,觉得可以加强一下功能,不由得动了一下脑筋,如下: 原文题目: 题目:给一个不多 ...