C#中可枚举类型详解

2019-12-30 18:12:21刘景俊

枚举是迭代一个集合中的数据项的过程。

我们经常使用的大多数集合实际上都已经实现了枚举的接口IEnumerable和IEnumerator接口,这样才能使用foreach迭代,有些是含有某种抽象了枚举细节的接口:ArrayList类型有索引,BitArray有Get方法,哈希表和字典有键和值..........其实他们都已经实现了IEnumerable和IEnumerator接口。所以一切的集合和数组都可以用IEnumerable或者IEnumerable<T>接口来定义。


 IEnumerable lists1 = new int[] { 3, 4, 5 };
      foreach(var val in lists1)
      {
        Console.WriteLine(val);
      }
      IEnumerable<int> lists2=new int[]{1,2,3};
      foreach(var val in lists2)
      {
        Console.WriteLine(val);
      }

下面讲解一下 自己来定义可枚举类型(简单说就是自己定义的 ,可以进行foreach迭代的集合):

因为枚举非常有好处,可以消除很多的错误,所以实现某种标准是有好处的。这种标准就是IEnumerable和IEnumerator接口,必须实现了它才能够使用foreach迭代,才能真正算是一个自己定义的,功能健全的集合。

我们自己建立的可枚举类型必须实现IEnumerable和IEnumerator接口(其实两者都有一个泛型实现)。

IEnumerable接口含有一个方法,该方法返回一个枚举器对象,枚举器对象实现了IEnumerator接口(实际上可以认为继承和实现了IEnumerator的接口的类的对象就是枚举器对象),可以用它来进行迭代。

下面是两个接口的定义(系统早已经定义好):


 public interface IEnumerable
  {
    IEnumerator GetEnumerator();
  }

该接口只有一个GetEnumerator的方法,返回一个枚举器,用于枚举集合中的元素。 


 public interface IEnumerator
  {
    object Current { get; };//Current属性返回集合的当前元素
    bool MoveNext();    //将枚举移动到下一位
    void Reset();     //使枚举回到开头
  }

凡是继承和实现了上面这个接口的类对象就是枚举器,可以利用上面的三个方法进行枚举,非常安全。不过需要自己在继承了接口的代码中去写实现过程。

一般的情况是:枚举器是枚举模式的一部分,通常被实现为枚举类型(继承IEnumerable)的一个嵌套类(继承IEnumerator)。嵌套类的好处就是可以访问外部类的私有成员,不破坏封装的原则。

下面我们自己来定义一个枚举类型,代码如下:


public class SimpleCollection :IEnumerable
  {
    //定义一个数组的字段
    private object[] array;

    //定义一个构造函数
    public SimpleCollection(object []items)
    {
      array = items;
    }
    //实现IEnumerable接口的GetNumerator方法 该方法返回一个继承IEnumerator接口的类的实例
    public  IEnumerator GetEnumerator()
    {
      return  new Enumerator(array);
    }
    //定义一个嵌套类来继承IEnumerator的接口
    public class Enumerator : IEnumerator
    {
      //定义一个标记字段
      private int flag;
      //定义一个数组的字段
      private object[] elements = null;
      //定义一个构造函数
      public Enumerator(object []items)
      {
        elements = items;
        flag = -1; //将标记位初始化
        
        //也可以采用下面的方法
        //elements = new object[items.Length];
        //Array.Copy(items, elements, items.Length);//此静态方法用于将一个数组中的元素复制到另外一个数组
      }
      //实现IEnumerator接口的Current属性; 此属性返回集合的当前元素,是只读的
      public object Current
      {
        get
        {
          if (flag > elements.Length - 1) throw new InvalidOperationException("枚举已经结束");
          else if (flag < 0) throw new InvalidOperationException("枚举尚未开始");
          else return elements[flag];
        }
      }
      //实现IEnumerator接口的MoveNext方法 将枚举移动到下一位
      public bool MoveNext()
      {
        ++flag;
        if (flag > (elements.Length - 1)) return false;
        else return true;
      }
      //实现IEnumerator接口的Reset方法 使枚举回到开头
      public void Reset()
      {
        flag = -1;
      }
    }