迭代器的使用
参考文章如下:
1、C#中的迭代器的作用 http://zhidao.baidu.com/question/110561432.html
2、详解C#迭代器 http://www.cnblogs.com/yangecnu/archive/2012/03/17/2402432.html
3、msdn 迭代器 http://msdn.microsoft.com/zh-cn/library/dscyy5s0(VS.80).aspx
4、C# IEnumerable和IEnumerator的区别 http://www.cnblogs.com/shaosks/archive/2011/09/27/2193270.html
一、迭代器的作用
看了好几篇文章,也翻了翻《C#4.0图解教程》,还是觉得如下这段写的不错
你需要理解什么是数组Array,什么是集合IConnection,什么是IList开始.
举个简单例子:
数组如 string ar=new string[]{"a","b","c"}
这是一个简单字符串数组。
假设我需要提取数组中的每一个元素,我该怎么办呢?
所以那些天才们就想出一种方案,用C#语法表达是:
forech(string item in ar)
{
MessageBox.Show(item);
}
然后你可以把forech(string item in ar)这种语法理解为迭代器.
专业解释:
1.迭代器是可以返回相同类型值的有序序列的一段代码;
2.迭代器可用作方法、运算符或get访问器的代码体;
3.迭代器代码使用yield return语句依次返回每个元素,yield break将终止迭代;
4.可以在类中实现多个迭代器,每个迭代器都必须像任何类成员一样有惟一的名称,并且可以在foreach语句中被客户端代码调用;
5.迭代器的返回类型必须为IEnumerable和IEnumerator中的任意一种;
6.迭代器是产生值的有序序列的一个语句块,不同于有一个 或多个yield语句存在的常规语句块;
7.迭代器不是一种成员,它只是实现函数成员的方式,理解这一点是很重要的,一个通过迭代器实现的成员,可以被其他可能或不可能通过迭代器实现的成员覆盖和重载;
8.迭代器块在C#语法中不是独特的元素,它们在几个方面受到限制,并且主要作用在函数成员声明的语义上,它们在语法上只是语句块而已;
迭代器模式是设计模式中行为模式(behavioral pattern)的一个例子,他是一种简化对象间通讯的模式,也是一种非常容易理解和使用的模式。简单来说,迭代器模式使得你能够获取到序列中的所有元素而不用关心是其类型是array,list,linked list或者是其他什么序列结构。这一点使得能够非常高效的构建数据处理通道(data pipeline)--即数据能够进入处理通道,进行一系列的变换,或者过滤,然后得到结果。事实上,这正是LINQ的核心模式。
在C#1中已经内建了对迭代器的支持,那就是foreach语句。使得能够进行比for循环语句更直接和简单的对集合的迭代,编译器会将foreach编译来调用GetEnumerator和MoveNext方法以及Current属性,如果对象实现了IDisposable接口,在迭代完成之后会释放迭代器。但是在C#1中,实现一个迭代器是相对来说有点繁琐的操作。C#2使得这一工作变得大为简单,节省了实现迭代器的不少工作。
二、简单代码实现:
《深入理解C# 第二版》中关于C#1.0 实现迭代其的代码如下:
(1)声明IEnumerable接口
1 public class IterationSample:IEnumerable 2 { 3 public object[] values; 4 public int startingPoint; 5 public IterationSample(object[] values,int startingPoint) 6 { 7 this.values = values; 8 this.startingPoint = startingPoint; 9 } 10 public IEnumerator GetEnumerator() 11 { 12 return new IterationSampleIterator(this) ; 13 } 14 }
(2)实现IEnumerator接口
1 class IterationSampleIterator:IEnumerator 2 { 3 IterationSample parent; 4 int position; 5 internal IterationSampleIterator(IterationSample parent) 6 { 7 this.parent = parent; 8 position = -1; 9 } 10 public bool MoveNext() 11 { 12 if(position!=parent.values.Length) 13 { 14 position++; 15 } 16 return position < parent.values.Length; 17 } 18 public object Current 19 { 20 get 21 { 22 if (position == -1 || position == parent.values.Length) 23 { 24 throw new InvalidOperationException(); 25 } 26 int index = position + parent.startingPoint; 27 index = index % parent.values.Length; 28 return parent.values[index]; 29 } 30 } 31 public void Reset() 32 { 33 position = -1; 34 } 35 }
(3)Main()方法中的代码实现
1 static void Main(string[] args) 2 { 3 bool isGo = true; 4 while (isGo) 5 { 6 Console.WriteLine("输入一个起始位置(1-5之间)"); 7 int startPoint = int.Parse(Console.ReadLine()); 8 object [] values = { "a", "b", "c", "d", "e" }; 9 IterationSample it = new IterationSample(values, startPoint-1); 10 foreach (object item in it) 11 { 12 Console.WriteLine(item); 13 } 14 Console.WriteLine("是否继续(Y/N)"); 15 switch (Console.ReadLine()) 16 { 17 case "Y": 18 isGo = true; 19 break; 20 case "y": 21 isGo = true; 22 break; 23 case "N": 24 isGo = false; 25 break; 26 case "n": 27 isGo = false; 28 break; 29 } 30 } 31 Console.ReadKey(); 32 } 33 }
我们发现这样手写来实现迭代器的功能代码量确实有写太大了,那么在C#2.0中就采用yield语句简单迭代
1 class IterationSample:IEnumerable 2 { 3 object[] values; 4 int startingPoint; 5 public IterationSample(object[]values,int startingPoint) 6 { 7 this.values = values; 8 this.startingPoint = startingPoint; 9 } 10 public IEnumerator GetEnumerator() 11 { 12 for (int index = 0; index < values.Length;index++ ) 13 { 14 yield return values[(index+startingPoint)%values.Length]; 15 } 16 } 17 } 18 19 static void Main(string[] args) 20 { 21 object[] values = { "a","b","c","d","e"}; 22 int startPoint = 2; 23 IterationSample it = new IterationSample(values,startPoint); 24 foreach (object item in it) 25 { 26 Console.WriteLine(item); 27 } 28 Console.ReadKey(); 29 }
三、对以上的总结:以上是不使用接口的枚举数
缺点:
1、Current返回的对象是object类型,对于值类型而言,在由Current返回之前必须装箱成object,在从Current获取之后,又必须再一次拆箱,如果需要操作大量的数据,会打来严重的性能问题、
2、类型不安全。值被作为对象来枚举,所以可以是任何类型,这就消除了编译时的类型检测。
四、泛型接口 IEnumerator<T> IEnumerable<T>
IEnumerator<T>接口使用泛型来返回实际的类型,而不是object类型的引用
1 class ColorEnumerator : IEnumerator<string> 2 { 3 string[] Colors; 4 int Position = -1; 5 public ColorEnumerator(string [] colors) 6 { 7 Colors=new string[colors.Length]; 8 for (int i = 0; i < colors.Length;i++ ) 9 { 10 Colors[i] = colors[i]; 11 Console.WriteLine(Colors[i]); 12 } 13 } 14 public string Current 15 { 16 get 17 { 18 return Colors[Position]; 19 } 20 } 21 object IEnumerator.Current 22 { 23 get 24 { 25 return Colors[Position]; 26 } 27 } 28 public bool MoveNext() 29 { 30 if (Position < Colors.Length - 1) 31 { 32 Position++; 33 return true; 34 } 35 else 36 { 37 return false; 38 } 39 } 40 public void Reset() 41 { 42 Position = -1; 43 } 44 public void Dispose() 45 { 46 this.Dispose(); 47 } 48 }
IEnumerable
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Collections; 6 //using System.Collections.Generic; 7 8 namespace IEnumerator_T 9 { 10 class Program 11 { 12 static void Main(string[] args) 13 { 14 //string[] colors = { "Red","Green","Blue"}; 15 //ColorEnumerator col = new ColorEnumerator(colors); 16 //Console.ReadKey(); 17 MyColors my = new MyColors(); 18 foreach (string item in my) 19 { 20 Console.WriteLine(item); 21 } 22 Console.ReadKey(); 23 } 24 } 25 26 class ColorEnumerator : IEnumerator<string> 27 { 28 string[] Colors; 29 int Position = -1; 30 public ColorEnumerator(string [] colors) 31 { 32 Colors=new string[colors.Length]; 33 for (int i = 0; i < colors.Length;i++ ) 34 { 35 Colors[i] = colors[i]; 36 // Console.WriteLine(Colors[i]); 37 } 38 } 39 public string Current 40 { 41 get 42 { 43 return Colors[Position]; 44 } 45 } 46 object IEnumerator.Current 47 { 48 get 49 { 50 return Colors[Position]; 51 } 52 } 53 public bool MoveNext() 54 { 55 if (Position < Colors.Length - 1) 56 { 57 Position++; 58 return true; 59 } 60 else 61 { 62 return false; 63 } 64 } 65 public void Reset() 66 { 67 Position = -1; 68 } 69 public void Dispose() 70 { 71 // this.Dispose(); 72 } 73 } 74 class MyColors:IEnumerable<string> 75 { 76 string[] Colors = { "Red","Yellow","Blue"}; 77 public IEnumerator<string> GetEnumerator() 78 { 79 return new ColorEnumerator(Colors); 80 } 81 IEnumerator IEnumerable.GetEnumerator() 82 { 83 return new ColorEnumerator(Colors); 84 } 85 } 86 }
五、由于上面的种种不便,迭代器开始闪亮登场了
1、迭代器可自动帮助生成可枚举类型和枚举数
使用迭代器来创建枚举数
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 使用迭代器创建可枚举数 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 MyClaa mc = new MyClaa(); 13 foreach (string item in mc) 14 { 15 Console.WriteLine(item); 16 } 17 Console.ReadKey(); 18 } 19 } 20 class MyClaa 21 { 22 public IEnumerator<string> GetEnumerator() 23 { 24 return BlackAndWhite(); 25 } 26 public IEnumerator<string> BlackAndWhite() 27 { 28 yield return "blcak"; 29 yield return "white"; 30 yield return "gray"; 31 } 32 } 33 }
BlackAndWhite迭代器方法返回IEnumerator<string>,MyClaa类通过返回由BlackAndWhite返回的对象来实现GetEnumerator方法
使用迭代器来创建可枚举类型
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 namespace 使用迭代器来创建可枚举类型 7 { 8 class Program 9 { 10 static void Main(string[] args) 11 { 12 MyClass my = new MyClass(); 13 foreach (string item in my) 14 { 15 Console.WriteLine(item); 16 } 17 //foreach (string item in my.BlackAndWhite) 18 //{ 19 20 //} 21 Console.ReadKey(); 22 } 23 } 24 class MyClass 25 { 26 public IEnumerator<string> GetEnumerator() 27 { 28 //获取可枚举对象 29 IEnumerable<string> myEnumerable = BlackAndWhite(); 30 //获取可枚举数 31 return myEnumerable.GetEnumerator(); 32 } 33 public IEnumerable<string> BlackAndWhite() 34 { 35 yield return "black"; 36 yield return "gray"; 37 yield return "green"; 38 } 39 } 40 }
六、总结一下:
(1)简单来说IEnumerable是声明式接口
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
(2)IEnumerator是实现式接口
public interface IEnumerator
{
object Current(get;);
bool MoveNext();
void Reset();
}
(3)Collection要支持foreach进行遍历就必须实现IEnumerable,并以某种方式返回迭代器对象IEnumerator 。