官方文档例子:
using System; using System.Collections; public class Person { public Person(string fName, string lName) { this.firstName = fName; this.lastName = lName; } public string firstName; public string lastName; } public class People : IEnumerable { private Person[] _people; public People(Person[] pArray) { _people = new Person[pArray.Length]; for (int i = 0; i < pArray.Length; i++) { _people[i] = pArray[i]; } } IEnumerator IEnumerable.GetEnumerator() { return (IEnumerator) GetEnumerator(); } public PeopleEnum GetEnumerator() { return new PeopleEnum(_people); } } 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(); } } } } class App { static void Main() { Person[] peopleArray = new Person[3] { new Person("John", "Smith"), new Person("Jim", "Johnson"), new Person("Sue", "Rabon"), }; People peopleList = new People(peopleArray); foreach (Person p in peopleList) Console.WriteLine(p.firstName + " " + p.lastName); } } /* This code produces output similar to the following: * * John Smith * Jim Johnson * Sue Rabon * */
1、首先看一个简单的列子
1 int[] myArray = { 1, 32, 43, 343 }; 2 //很少这样写的 3 IEnumerator myie = myArray.GetEnumerator(); //获取需要遍历的枚举数 4 myie.Reset(); //重置 5 while (myie.MoveNext()) 6 { 7 int i = (int)myie.Current; //这边涉及一个装箱的操作 8 Console.WriteLine("Value: {0}", i); 9 } 10 //一般情况下是这样写的11 foreach(int item in myArray ) 12 { 13 Console.WriteLine(item.ToString()); 14 }
在大部分的情况下,很多人会使用for和foreach来遍历数组,而对于上面的语法却用的很少,但是对foreach的具体来历还很模糊!
2、先从foreach说起
大家都知道要实现foreach的必须要实现IEnumerable和IEnumerator的接口,只有实现了它们,才能实现遍历,所以要讲foreach的来历,必须要把那两个接口给搞清楚点!
这边也想说明一点的是:如果对这两个接口有了一定的了解后,只要实现那个GetEnumerator方法即可,而不需要实现于IEnumerable接口,这只是针对对这两个接口有一定了解的朋友!
3、IEnumerable
具体的作用:就是使实现这个接口的对象成为可枚举类型。
IEnumerable接口包含一个GetEnumerator方法,返回值为IEnumerator的类型!
代码如下:
1 public class MyColors : IEnumerable 2 { 3 string[] colors = { "Red", "Yellow", "Biue" }; 4 5 public IEnumerator GetEnumerator() 6 { 7 8 return colors.GetEnumerator(); 9 10 } 11 }
那么我们在客户端进行调用的时候就可以这样做了!
1 MyColors colors = new MyColors(); 2 foreach (string item in colors) 3 { 4 Console.WriteLine(item); 5 }
这 样就能 使用实现这个接口的对象进行foreach遍历了,就是这么简单的一个列子,这边可能有的人会有疑问,我们就实现了IEnumerable接口但却没实现 IEnumerator接口,可是我们不是说只有实现了这两个接口才可以进行foreach遍历吗?可以这样说当使用forach进行遍历的时候,编译器 会到这个对象的类中去寻找GetEnumerator方法,找到这个方法后返回这个一个IEnumerator的对象(枚举数),而我这边返回的是 “colors.GetEnumerator()”,是一个数组对象调用它本身中的“GetEnumerator”方法,所以说数组本身就实现了 IEnumerator接口,那么两个接口都实现了,不就好实现foreach遍历了,其实在实现遍历枚举数的时候编译器会自动去调用数组中实现枚举数的 类中的方法(这将在下一篇中讲到)!
4、IEnumerator
接口的作用:实现可枚举数
首先看一下接口的定义:
包含一个属性两个方法
MoveNext → 把当前的项移动到下一项(类似于索引值),返回一个bool值,这个bool值用来检查当前项是否超出了枚举数的范围!
Current → 获取当前项的值,返回一个object的类型(这边会涉及到装箱和拆箱的问题 → 后面讲泛型的时候会涉及到)!
Reset → 顾名思义也就是把一些值恢复为默认值,比如把当前项恢复到默认状态值!
代码如下:
1 public class MyIEnumerator : IEnumerator 2 { 3 4 string[] colors; //定义一个数组,用来存储数据 5 int position = -1; //定义当前项的默认值,也就是索引值,一开始认识数组的索引从“0”开始,怎么默认设置他为“-1”呢,最后才想明白,这样设置是合情合理的! 6 7 public MyIEnumerator(string[] colors) 8 { 9 this.colors = new string[colors.Length]; 10 11 for (int i = 0; i < this.colors.Length; i++) 12 { 13 this.colors[i] = colors[i]; 14 } 15 } 16 17 public object Current //根据当前项获取相应的值18 { 19 get 20 { 21 return colors[position]; //返回当前项的值,但是会做一个装箱的操作!22 } 23 } 24 25 public bool MoveNext() //移动到下一项26 { 27 if (position < colors.Length - 1) //这就是设置默认值为-1的根据28 { 29 position++; 30 return true; 31 } 32 else 33 { 34 return false; 35 } 36 } 37 38 public void Reset() //重置当前项的值,恢复为默认值39 { 40 this.position = -1; 41 } 42 }
在 第三点讲到的 IEnumerable接口中GetEnumerator方法是获取要遍历的枚举数,在我们没有创建自己的遍历枚举数的类时,我们使用的是Array的遍 历枚举数的方法(关于数组的可枚举类型和枚举数会在下一篇讲到),但这个有的时候不一定适合我们,我们需要为自己定制一个更合适的,所以我们要创建自己的 枚举数类(也就是上面的代码),把第三点和第四点的代码合并起来(改动一点代码),如下:
1 public class MyColors //: IEnumerable 2 { 3 string[] colors = { "Red", "Yellow", "Biue" }; 4 5 public IEnumerator GetEnumerator() 6 { 7 return new MyIEnumerator(colors); 8 9 //这边就是我们要改动的地方,返回一个自己创建的类型,不是在返回数组的IEnumerator对象了,而是我们自己创建的枚举数对象→MyIEnumerator10 } 11 }
客户端是不要改动代码的,然后只要使用foreach直接遍历就可以了!
5、关于可枚举类型和枚举数
①可枚举类型 → 实现IEnumerable接口,可以不需要直接实现这个接口,但必须有个GetEnumerator方法,返回值类型必须为IEnumerator类型,也就是第四点最后一段代码中接口注释的那种写法!
②枚举数 → 实现IEnumerator接口,实现全部方法,首先是调用GetEnumerator返回一个类型为IEnumerator的枚举数,然后编译器会隐式的调用实现IEnumerator类中的方法和属性!
总结:所以实现foreach遍历,必须达到上面的两种条件才能进行遍历对象,他们可以写在一起也可以分开,最好是分开,进行职责分离,一个类干一件事总归是好事!
来源:http://www.cnblogs.com/yangcaogui/archive/2011/12/04/2266589.html
/// <summary> /// 处理数据源,转换成IEnumerator,方便进行下一步操作 /// </summary> /// <param name="source">数据源,先统一装箱</param> /// <returns>数据源的IEnumerable形式</returns> private IEnumerator getResolvedDataSource(object source) { if (source is IEnumerator) return (IEnumerator)source; else if (source is IList) return ((IList)source).GetEnumerator(); else if (source is DataSet) return (((DataSet)source).Tables[0].DefaultView.GetEnumerator()); else if (source is DataTable) return ((DataTable)source).DefaultView.GetEnumerator(); else return null; }
相关推荐
Foreach常用于循环访问集合,对实现IEnumerable的接口的容器进行遍历,IEnumerable和IEnumerator接口我有时候也有点迷糊,按官方的解释,IEnumerable是枚举器接口,IEnumerator是迭代器接口,从字面意思来看相差不大...
### C#自建类实现集合接口 `IEnumerable` 和 `IEnumerator` 在C#中,自定义一个集合类并使其能够支持常见的集合操作(如迭代、添加、删除等)是非常实用的功能。下面将详细介绍如何通过实现 `IEnumerable` 和 `...
此外,尽管使用IEnumerable和IEnumerator接口可以实现枚举,但这种方式存在性能和类型安全问题。当Current返回object类型时,对于值类型的数据需要进行装箱和拆箱,大量操作时会影响效率。同时,由于返回的是object...
当需要遍历集合或自定义数据流时,可以使用IEnumerable接口和IEnumerator接口。这两个接口提供了遍历数据集的能力。 1. 让类继承`IEnumerable<T>`或`IEnumerable`接口,这样你的类就成为了可迭代的对象。这将强制你...
我们经常使用的大多数集合实际上都已经实现了枚举的接口IEnumerable和IEnumerator接口,这样才能使用foreach迭代,有些是含有某种抽象了枚举细节的接口:ArrayList类型有索引,BitArray有Get方法,哈希表和字典有键...
如果该类型是一个泛型集合,还可能需要实现IEnumerable和IEnumerator接口以支持类型安全的枚举。 具体到代码实现,我们可以参考文章中给出的Person类的示例。Person类包含一个字符串数组_names,如果我们希望...
IEnumerator接口包含三个方法:`MoveNext()`、`Reset()`和`Current`属性。 - `MoveNext()`:这个方法将枚举器向前移动到集合中的下一个元素。如果已到达集合的末尾,`MoveNext()`将返回`false`。 - `Reset()`:此...
在C#中,`IEnumerable`和`IEnumerator`接口被用来支持集合的遍历。通过实现这些接口,可以创建可枚举的集合。 ##### 1. `IEnumerable`接口 - **定义**:`IEnumerable`接口用于声明一个集合是可枚举的。这意味着...
`IEnumerator<T>`接口提供了`MoveNext()`、`Reset()`和`Current`属性,用于控制遍历过程和访问当前元素。`IEnumerable<T>`和`IEnumerable`接口的区别在于前者是泛型,后者是为非泛型集合设计的,但两者都支持`...
要使一个类支持foreach遍历,需要实现IEnumerable和IEnumerator接口。这些接口提供了遍历集合的机制。可以通过实现GetEnumerator方法和MoveNext、Reset等方法来实现遍历。 7. 程序中有什么错? 在静态函数中只能...
一、实现foreach的关键: 必须为包含有GetEnumerator()...四、如果返回类型为IEnumerator编译时会实现一个实现了IEnumerator接口的类 五、同理如果返回类型为IEnumerable编译时会实现一个实现了IEnumerable接口的类
- **集合接口使用** - 使用`List<T>`集合来存储水果对象。 - 实现`IEnumerable`接口,并重写`GetEnumerator()`方法。 - `GetEnumerator()`方法返回一个实现了`IEnumerator`接口的枚举器对象,用于迭代集合中的...
本篇文章将深入探讨如何通过自定义类型实现四个关键的系统接口:`IComparable`、`IComparer`、`IEnumerable`和`IEnumerator`以及`IDisposable`。这些接口在各种场景下都有广泛的应用,例如数据排序、枚举遍历和资源...
C#中的迭代器封装在IEnumerable和IEnumerator和他们的泛型接口中。 IEnumerable:定义了一个可以获取IEnumerator的方法—GetEnumerator()。 //IEnumerable的代码实现 public interface IEnumerable { ...
总的来说,`IEnumerable`接口和`IEnumerator`接口是C#中实现迭代的关键,它们使得开发者能够方便地遍历任何可枚举类型的集合,包括自定义的集合类型。在给定的例子中,通过自定义迭代器,我们能够以反向顺序遍历字符...
本文将深入解析C#中的一些关键非泛型集合接口,如`IEnumerable`、`IEnumerator`、`IComparer`、`IEqualityComparer`、`IStructuralComparable`、`IStructuralEquatable`、`ICollection`、`IList`以及`IDictionary`...
.NET中存在两种类型的枚举器:非泛型的`IEnumerator/IEnumerable`接口和泛型的`IEnumerator<T>/IEnumerable<T>`接口。非泛型版本可以返回任何类型的元素,但可能导致类型转换的开销,而泛型版本则提供了类型安全的...