C# 枚举器 手动实现枚举器 可枚举集合 枚举器操作 迭代器
2011-09-24 14:31
525 查看
在C#语言中提供 foreach 查询操作,foreach 大大的简化了要编写的代码,但是foreach 只能用来遍历一个可枚举的集合(enumable),可枚举的集合就是实现了System.Collections.IEnumerable接口的一个集合。
但是foreach 返回的是可枚举对象的一个只读集合,不可以通过foreach 来修改可枚举对象。
简单的一个例子:
int 类型,继承了IEnumerable<T> 接口,所有可以用foreach 来遍历。如果我们自己写的一个类,没有实现IEnumerable<T> 接口,用foreach 就不能进行迭代了,编译器会报错。
例如:
自己写的一个类:
这个类实现了 IComparable<T> 接口,但是没有实现 IEnumerable<T> 接口,不能用foreach 遍历。
下面的代码对这个没有实现 IEnumerable<T> 接口的类进行foreach 遍历结果发生编译错误。
下面我们将自己手动的给上面那个简单的类实现枚举器,通过foreach来遍历这个类。
在 IEnumerable接口中,包括一个GetEnumerator的方法:
这个方法返回一个枚举器对象。
IEnumerator 接口中规定了以下方法和属性:
最初枚举器的指针指向枚举集合的第一个元素的前面,在获取 Current之前先要调用MoveNext() 使指针指向下一个元素。
以上所说的接口, 都有他们对应的泛型接口,在接下来的实现中,都会使用泛型接口。
当我们给要枚举的类实现了IEnumerable<T>接口后,foreach语句会自动的让生成一个枚举器,并通过枚举器中的Current, MoveNext等获取下一个数据,这一切过程对于foreach的使用者都是透明的。
下面,先给出本实例的所有代码,然后进行分析讲解。
所有原码如下:
首先,我们定义了一个Student 类,并实现了IComparable<T> 接口,我们程序的目的就是在使用Student类的时候,可以通过foreach 来遍历Student数组中的每个成员,但是默认情况下Student类并没有实现IEnumberable<T>接口,所有在直接用foreach 遍历的时候会编译报错。
为了遍历Student类,我们手动实现了一个枚举器。
在MOveNext() 中,如果这个枚举器的MyQueue为null,则new 一个Queue, 用MyQueue存放给枚举器传进来的对象数组。每一次MoveNext() 都会造成一个对象出队。
然后给一个类实现IEnumerable<T> 接口,通过GetEnumerator() 方法获取枚举器:
这样,在我们以后创建一个Sector 对象以后,就可以直接用foreach 对Sector对象中的Student 对象进行枚举:
但是foreach 返回的是可枚举对象的一个只读集合,不可以通过foreach 来修改可枚举对象。
简单的一个例子:
int[] number_array = new int[] { 1,2,3,4,5,6}; foreach (int k in number_array) { Console.WriteLine(k); }
int 类型,继承了IEnumerable<T> 接口,所有可以用foreach 来遍历。如果我们自己写的一个类,没有实现IEnumerable<T> 接口,用foreach 就不能进行迭代了,编译器会报错。
例如:
自己写的一个类:
class Student: IComparable<Student> { public Student() { // nothing } private int age; public int Age { get { return this.age;} set { this.age = value; } } public int CompareTo(Student other) { //throw new NotImplementedException(); if (this.age == other.age) { return 0; } else if (this.age < other.age) { return -1; } else { return 1; } } }
这个类实现了 IComparable<T> 接口,但是没有实现 IEnumerable<T> 接口,不能用foreach 遍历。
下面的代码对这个没有实现 IEnumerable<T> 接口的类进行foreach 遍历结果发生编译错误。
//Student[] student=new Student[5]; //student.test(); //foreach (Student t in student) //{ //}
下面我们将自己手动的给上面那个简单的类实现枚举器,通过foreach来遍历这个类。
在 IEnumerable接口中,包括一个GetEnumerator的方法:
IEnumerator GetEnumertor();
这个方法返回一个枚举器对象。
IEnumerator 接口中规定了以下方法和属性:
object Current {get;} bool MoveNext(); void Reset();
最初枚举器的指针指向枚举集合的第一个元素的前面,在获取 Current之前先要调用MoveNext() 使指针指向下一个元素。
以上所说的接口, 都有他们对应的泛型接口,在接下来的实现中,都会使用泛型接口。
当我们给要枚举的类实现了IEnumerable<T>接口后,foreach语句会自动的让生成一个枚举器,并通过枚举器中的Current, MoveNext等获取下一个数据,这一切过程对于foreach的使用者都是透明的。
下面,先给出本实例的所有代码,然后进行分析讲解。
所有原码如下:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace EnumerableTest { class Program { static void Main(string[] args) { int[] number_array = new int[] { 1,2,3,4,5,6}; foreach (int k in number_array) { Console.WriteLine(k); } //Student[] student=new Student[5]; ////student.test(); //foreach (Student t in student) //{ //} Student[] student = new Student[5]; student[0] = new Student(); student[0].Age = 12; student[1] = new Student(); student[1].Age = 13; student[2] = new Student(); student[2].Age = 74; student[3] = new Student(); student[3].Age = 34; student[4] = new Student(); student[4].Age = 32; Console.WriteLine("/////////////////////////////////////"); Sector<Student> sector = new Sector<Student>(student); foreach (Student t in sector) { Console.WriteLine(t.Age); } } } class Sector<T> : IEnumerable<T> where T : IComparable<T> { T[] student; public Sector(T[] t) { student = t; } public IEnumerator<T> GetEnumerator() { return new SectorEnumator<T>(student); //throw new NotImplementedException(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { //throw new NotImplementedException(); return null; } } class SectorEnumator<T> : IEnumerator<T> where T : IComparable<T> { private T[] student; private Queue<T> MyQueue; private T currentItem; public SectorEnumator(T[] s) { this.student = s; } public T Current { get { return this.currentItem; } } public void Dispose() { // simply do nothing //throw new NotImplementedException(); } object System.Collections.IEnumerator.Current { get { throw new NotImplementedException(); } } public bool MoveNext() { if (this.MyQueue == null) { MyQueue = new Queue<T>(); populate(student); } if (this.MyQueue.Count > 0) { this.currentItem = MyQueue.Dequeue(); return true; } return false; //throw new NotImplementedException(); } public void Reset() { //throw new NotImplementedException(); MyQueue.Clear(); populate(student); } private void populate(T[] s) { int num = s.Length; for (int i = 0; i < num; i++) { MyQueue.Enqueue(s[i]); } } } class Student: IComparable<Student> { public Student() { // nothing } private int age; public int Age { get { return this.age;} set { this.age = value; } } public int CompareTo(Student other) { //throw new NotImplementedException(); if (this.age == other.age) { return 0; } else if (this.age < other.age) { return -1; } else { return 1; } } } }
首先,我们定义了一个Student 类,并实现了IComparable<T> 接口,我们程序的目的就是在使用Student类的时候,可以通过foreach 来遍历Student数组中的每个成员,但是默认情况下Student类并没有实现IEnumberable<T>接口,所有在直接用foreach 遍历的时候会编译报错。
为了遍历Student类,我们手动实现了一个枚举器。
class SectorEnumator<T> : IEnumerator<T> where T : IComparable<T> { private T[] student; private Queue<T> MyQueue; private T currentItem; public SectorEnumator(T[] s) { this.student = s; } public T Current { get { return this.currentItem; } } public void Dispose() { // simply do nothing //throw new NotImplementedException(); } object System.Collections.IEnumerator.Current { get { throw new NotImplementedException(); } } public bool MoveNext() { if (this.MyQueue == null) { MyQueue = new Queue<T>(); populate(student); } if (this.MyQueue.Count > 0) { this.currentItem = MyQueue.Dequeue(); return true; } return false; //throw new NotImplementedException(); } public void Reset() { //throw new NotImplementedException(); MyQueue.Clear(); populate(student); } private void populate(T[] s) { int num = s.Length; for (int i = 0; i < num; i++) { MyQueue.Enqueue(s[i]); } } }
在MOveNext() 中,如果这个枚举器的MyQueue为null,则new 一个Queue, 用MyQueue存放给枚举器传进来的对象数组。每一次MoveNext() 都会造成一个对象出队。
然后给一个类实现IEnumerable<T> 接口,通过GetEnumerator() 方法获取枚举器:
class Sector<T> : IEnumerable<T> where T : IComparable<T> { T[] student; public Sector(T[] t) { student = t; } public IEnumerator<T> GetEnumerator() { return new SectorEnumator<T>(student); //throw new NotImplementedException(); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { //throw new NotImplementedException(); return null; } }
这样,在我们以后创建一个Sector 对象以后,就可以直接用foreach 对Sector对象中的Student 对象进行枚举:
Sector<Student> sector = new Sector<Student>(student); foreach (Student t in sector) { Console.WriteLine(t.Age); }
相关文章推荐
- C# 枚举器 手动实现枚举器 可枚举集合 枚举器操作 迭代器
- C#枚举器,手动实现枚举器,可枚举集合,枚举器操作,迭代器
- 【C#】集合已修改;可能无法执行枚举操作。
- [C#]集合已修改;可能无法执行枚举操作
- C#学习之用迭代器实现枚举器
- C# 集合已修改;可能无法执行枚举操作
- C# 集合已修改;可能无法执行枚举操作
- C# 集合已修改;可能无法执行枚举操作
- C#中foreach出现“集合已修改 可能无法执行枚举操作”的解决方法
- NET 集合已修改 可能无法执行枚举操作 C#
- C#使用List出现“集合已修改;可能无法执行枚举操作”错误的解决办法
- C# 迭代器实现枚举器
- C# 集合类Dictionary的遍历和修改(防止错误:集合已修改;可能无法执行枚举操作。)
- C#学习之用迭代器实现枚举器
- C#中出现“集合已修改;可能无法执行枚举操作”错误 foreach list.Remove()
- C# 如何避免异常”集合已修改;可能无法执行枚举操作。“
- 黄聪:C#使用能够foreach对hashtable、List遍历时“集合已修改;可能无法执行枚举操作。”错误
- 手动实现枚举器、泛型集合、单链表
- C# 集合已修改 可能无法执行枚举操作 zz
- 怎么回事儿?C#错误:集合已修改;枚举操作可能无法执行。