IEnumerable和IEnumerable<T>接口
IEnumerable和IEnumerable<T>接口在.NET中是非常重要的接口,它允许开发人员定义foreach语句功能的实现并支持非泛型方法的简单的迭代,IEnumerable和IEnumerable<T>接口是.NET Framework中最基本的集合访问器,这两个接口对于LINQ的理解是非常重要的。
在面向对象的开发过程中,常常需要创建若干对象,并进行对象的操作和查询,在创建对象前,首先需要声明一个类为对象提供描述,示例代码如下所示。
using System; using System.Collections.Generic; using System.Linq; //使用LINQ命名控件 using System.Text; namespace IEnumeratorSample { class Person //定义一个Person类 { public string Name; //定义Person的名字 public string Age; //定义Person的年龄 public Person(string name, string age) //为Person初始化(构造函数) { Name = name; //配置Name值 Age = age; //配置Age值 } } }
上述代码定义了一个Person类并抽象一个Person类的属性,这些属性包括Name和Age。Name和Age属性分别用于描述Person的名字和年龄,用于数据初始化。初始化之后的数据就需要创建一系列Person对象,通过这些对象的相应属性能够进行对象的访问和遍历,示例代码如下所示。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Person[] per = new Person[2] //创建并初始化2个Person对象 6 { 7 new Person("guojing","21"), //通过构造函数构造对象 8 new Person("muqing","21"), //通过构造函数构造对象 9 }; 10 foreach (Person p in per) //遍历对象 11 Console.WriteLine("Name is " + p.Name + " and Age is " + p.Age); 12 Console.ReadKey(); 13 } 14 } 15 }
上述代码创建并初始化了2个Person对象,并通过foreach语法进行对象的遍历。但是上述代码是在数组中进行查询的,就是说如果要创建多个对象,则必须创建一个对象的数组,如上述代码中的Per变量,而如果需要直接对对象的集合进行查询,却不能够实现查询功能。例如增加一个构造函数,该构造函数用户构造一组Person对象,示例代码如下所示。
1 private Person[] per; 2 public Person(Person[] array) //重载构造函数,迭代对象 3 { 4 per = new Person[array.Length]; //创建对象 5 for (int i = 0; i < array.Length; i++) //遍历初始化对象 6 { 7 per[i] = array[i]; //数组赋值 8 } 9 }
上述构造函数动态的构造了一组People类的对象,那么应该也能够使用foreach语句进行遍历,示例代码如下所示。
1 Person personlist = new Person(per); //创建对象 2 foreach (Person p in personlist) //遍历对象 3 { 4 Console.WriteLine("Name is " + p.Name + " and Age is " + p.Age); 5 }
在上述代码的foreach语句中,直接在Person类的集合中进行查询,系统则会报错“ConsoleApplication1.Person”不包含“GetEnumerator”的公共定义,因此foreach语句不能作用于“ConsoleApplication1.Person”类型的变量,因为Person类并不支持foreach语句进行遍历。为了让相应的类能够支持foreach语句执行遍历操作,则需要实现派生自类IEnumerable并实现IEnumerable接口,示例代码如下所示。
1 public IEnumerator GetEnumerator() //实现接口 2 { 3 return new GetEnum(_people); 4 }
为了让自定义类型能够支持foreach语句,则必须对Person类的构造函数进行编写并实现接口,示例代码如下所示。
1 class Person:IEnumerable //派生自IEnumerable,同样定义一个Personl类 2 { 3 public string Name; //创建字段 4 public string Age; //创建字段 5 public Person(string name, string age) //字段初始化 6 { 7 Name = name; //配置Name值 8 Age = age; //配置Age值 9 } 10 public IEnumerator GetEnumerator() //实现接口 11 { 12 return new PersonEnum(per); //返回方法 13 } 14 }
上述代码重构了Person类并实现了接口,接口实现的具体方法如下所示。
1 class PersonEnum : IEnumerator //实现foreach语句内部,并派生 2 { 3 public Person[] _per; //实现数组 4 int position = -1; //设置“指针” 5 public PersonEnum(Person[] list) 6 { 7 _per = list; //实现list 8 } 9 public bool MoveNext() //实现向前移动 10 { 11 position++; //位置增加 12 return (position < _per.Length); //返回布尔值 13 } 14 public void Reset() //位置重置 15 { 16 position = -1; //重置指针为-1 17 public object Current //实现接口方法 18 { 19 get 20 { 21 try 22 { 23 return _per[position]; //返回对象 24 } 25 catch (IndexOutOfRangeException) //捕获异常 26 { 27 throw new InvalidOperationException(); //抛出异常信息 28 } 29 } 30 } 31 }
上述代码实现了foreach语句的功能,当开发Person类初始化后就可以直接使用Personal类对象的集合进行LINQ查询,示例代码如下所示。
1 static void Main(string[] args) 2 { 3 Person[] per = new Person[2] //同样初始化并定义2个Person对象 4 { 5 new Person("guojing","21"), //构造创建新的对象 6 new Person("muqing","21"), //构造创建新的对象 7 }; 8 Person personlist = new Person(per); //初始化对象集合 9 foreach (Person p in personlist) //使用foreach语句 10 Console.WriteLine("Name is " + p.Name + " and Age is " + p.Age); 11 Console.ReadKey(); 12 }
从上述代码中可以看出,初始化Person对象时初始化的是一个对象的集合,在该对象的集合中可以通过LINQ直接进行对象的操作,这样做即封装了Person对象也能够让编码更加易读。在.NET Framework 3.5中,LINQ支持数组的查询,开发人员不必自己手动创建IEnumerable和IEnumerable<T>接口以支持某个类型的foreach编程方法,但是IEnumerable和IEnumerable<T>是LINQ中非常重要的接口,在LINQ中也大量的使用IEnumerable和IEnumerable<T>进行封装,示例代码如下所示。
public static IEnumerable<TSource> Where<TSource> (this IEnumerable<TSource> source,Func<TSource, Boolean> predicate) //内部实现 { foreach (TSource element in source) //内部遍历传递的集合 { if (predicate(element)) yield return element; //返回集合信息 } }
上述代码为LINQ内部的封装,从代码中可以看到,在LINQ内部也大量的使用了IEnumerable和IEnumerable<T>接口实现LINQ查询。IEnumerable原本就是.NET Framework中最基本的集合访问器,而LINQ是面向关系(有序N元组集合)的,自然也就是面向IEnumerable<T>的,所以了解IEnumerable和IEnumerable<T>对LINQ的理解是有一定帮助的
实例:
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 using System.Collections; 6 namespace IEnumeratorSample 7 { 8 9 class Person : IEnumerable 10 { 11 public string Name;//定义Person的名字 12 public string Age;//定义Person的年龄 13 public Person(string name, string age)//为Person初始化(构造函数) 14 { 15 Name = name;//配置Name值 16 Age = age; 17 18 } 19 private Person[] per; 20 public Person(Person[] array)//重载构造函数,迭代对象 21 { 22 per = new Person[array.Length];//创建对象 23 for (int i = 0; i < array.Length; i++)//遍历初始化对象 24 { 25 per[i] = array[i];//数组赋值 26 } 27 } 28 public IEnumerator GetEnumerator()//实现接口 29 { 30 return new PersonEnum(per); 31 } 32 } 33 class PersonEnum : IEnumerator//实现foreach语句内部,并派生 34 { 35 public Person[] _per;//实现数组 36 int position = -1;//设置“指针” 37 public PersonEnum(Person[] list) 38 { 39 _per = list;//实现list 40 } 41 public bool MoveNext()//实现向前移动 42 { 43 position++;//位置增加 44 return (position < _per.Length);//返回布尔值 45 } 46 47 public void Reset()//位置重置 48 { 49 50 position = -1;//重置指针为-1 51 } 52 public object Current//实现接口方法 53 { 54 get 55 { 56 try 57 { 58 return _per[position];//返回对象 59 } 60 catch (IndexOutOfRangeException)//捕获异常 61 { 62 throw new InvalidOperationException();//抛出异常信息 63 } 64 } 65 } 66 } 67 68 69 class Program 70 { 71 72 static void Main(string[] args) 73 { 74 Person[] per = new Person[2] 75 { 76 new Person("guojing","21"), 77 new Person("muqing","21"), 78 }; 79 Person personlist = new Person(per); 80 foreach (Person p in personlist)//遍历对象 81 { 82 83 Console.WriteLine("Name is " + p.Name + " and Age is " + p.Age); 84 85 } 86 Console.ReadKey(); 87 } 88 } 89 }