给大家讲讲用于枚举元素集合的两个接口IEnumerator和IEnumerable。IEnumerator用于实现一个迭代器(相当于以前C++的文章中所说的iterator),它具备列举一个数据结构中所有元素所需要的一些方法和属性。而IEnumerable接口则用于返回一个迭代器对象,一个实现了IEnumerable的类型表示这个类型对象是可以枚举的。
下面我们先来看看这两个接口的定义吧!
IEnumerator
迭代器用于枚举一个数据结构中的所有元素。
namespace System.Collections
{
[ComVisible(true)]
[Guid("496B0ABE-CDEE-11d3-88E8-00902754C43A")]
public interface IEnumerable
{
[DispId(-4)]
IEnumerator GetEnumerator();
}
}
从上面的定义我们可以看到,一个Emurator具备了枚举一个数据结构中所有元素的最基本能力:
获取当前元素的的能力:Current属性;
移动元素指针的能力:MoveNext方法;
重置迭代器的能力:Reset方法。
这里的Current属性是Object类型,也就是可以返回所有类型元素,而与之对应的泛型接口是:System.Collections.Generic.IEnumerator<T>,它除了继承了Ienumerator之外,还增加了一个特定类型的Current属性。
IEnumerable
IEnumerable声明一个类型为可枚举的类型,而它的定义很简单,就是返回一个迭代器。
namespace System.Collections
{
[ComVisible(true)]
[Guid("496B0ABE-CDEE-11d3-88E8-00902754C43A")]
我们应该对C#风格的遍历语法应该很熟悉了,也就是foreach语句。说到foreach这个东西,其实在C++中也存在,但是是以函数的形式做在库里面的,而对C#来说,它已经被做到语言中去了。在绝大多数情形下,我们应该尽量使用foreach语句来遍历一个集合对象,而不是自己写一个for循环或者其他的while循环等,理由很简单:效率。而foreach语法需要被枚举的对象类型实现了IEnumerable接口。
与IEnumerable对应的泛型接口是:System.Collections.Generic.IEnumerable<T>。
设计一个集合类
通常,IEnumerator和IEnumerable是一起使用的。假设我们设计一个属于自己的一个数据结构类MyCollection,并且让他可以被枚举,那么整体上应该怎么设计呢?我们看看下面的代码。
class MyCollection:IEnumerable
{
public struct MyEmurator : IEnumerator
{
//此处省略实现代码
}
//此处省略部分实现代码
public IEnumerator GetEnumerator()
{
return new MyEmurator(this);
}
}
这是一个典型的对IEnumerator和IEnumerable的应用方式。几乎所有的System.Collection里面的容器都是都是这样来设计的。将容器类型本身实现IEnumerable,表明容器是可枚举的。而迭代器类型则是一个嵌套类型,通过容器类的接口函数GetEnumerator来返回迭代器的实例。通常一个容器和它的迭代器是紧密相关的,并且一个容器配备一个迭代器已经足以,那么将迭代器定义为嵌套类型,避免了管理的混乱。
实现一个2D List类型
我们这里说的二维List类型,其实就是实现一个以List为元素的List,简而言之,这个List2D就是用来存放List的一个List;但是我们枚举的时候,并不想要枚举List2D里面的list, 而是想直接枚举list里面的元素。
我们这里定义了一个泛型类List2D<T> ,实现了IEnumerable<T>接口。这里为了精简代码,这里没有让List2D实现IList接口,只提供了Add、Clear等几个简单方法。不多说了,还是来看看下面的代码吧!
class List2D<T> : IEnumerable<T>
{
//内嵌迭代器类型
public struct Emurator : IEnumerator<T>
{
//此处代码先不写出
}
private List<List<T>> _lists=new List<List<T>>();//存储列表
public List<T> this[int index]
{
get { return _lists[index]; }
}
public int Count
{
get
{
int count = 0;
foreach (List<T> list in _lists)
{
count += list.Count;
}
return count;
}
}
public void Add(List<T> item)
{
_lists.Add(item);
}
public void Clear()
{
_lists.Clear();
}
#region IEnumerable Members
public IEnumerator GetEnumerator()
{
return ((IEnumerable<T>)this).GetEnumerator();
}
#endregion
#region IEnumerable<T> Members
IEnumerator<T> IEnumerable<T>.GetEnumerator()
{
return new Emurator(this);
}
#endregion
}
|