帝国CMS模板大全
www.admin99.cn
www.92cms.cn 帝国CMS模板下载站!,情怀,养站,二次开发!源码需求比较大的一站式会员下载,价更省!!!

C#中可枚举类型详解

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

我们经常使用的大多数集合实际上都已经实现了枚举的接口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;

       }

     }

下面来延时如何使用枚举类型:

?

//下面来看枚举类型的使用

       SimpleCollection collection = new SimpleCollection( new object []{1,2,3,4,5});

 

       //使用方法

       //接口 变量名=继承了该接口的类的实例

       IEnumerator enumrator = collection.GetEnumerator();

 

       while (enumrator.MoveNext())

       {

        

         Console.WriteLine(enumrator.Current);

       }

       Console.ReadKey();

?

SimpleCollection simple = new SimpleCollection( new object [] { 1, 2, 3, 4, 5, 6 });

      IEnumerator enumerator = simple.GetEnumerator();

      while (enumerator.MoveNext())

      {

        Console.WriteLine(enumerator.Current);

      }

      //最重要的是,实现了那两个接口,我们就可以对我们的集合使用foreach迭代了,看下面

      foreach (var s in simple)

      {

        Console.WriteLine(s);

      }

下面给出两个接口的泛型实现:

首先需要注意的是:

1.IEnumerable<T>接口继承自IEnumerable      两者具有相同接口,所以必须实现泛型和非泛型版本的GetEumerator方法

2.IEnumerator<T>接口继承自IEnumerator和IDisposable  需要多实现泛型和非泛型版本的Current属性和IDisposable接口的Dispose方法。

代码如下:

 

?

////下面创建一个可枚举的泛类型

   //首先该类型必须要继承IEnumerable<T>接口

   //因为IEnumerable<T>接口继承IEnumerable接口 所以必须同时实现泛型和非泛型的GetEnumerator方法

   public class SimpleCollection<T> : IEnumerable<T>

   {

     private T[] array;

     public SimpleCollection(T[] items)

     {

       array = items;

     

     }

     //实现IEnumerable<T>接口的GetNumerator方法 该方法返回一个继承IEnumerator接口的类的实例

     public IEnumerator<T> GetEnumerator()

     {

       return new Enumerator<T>(array); //这步需要重视

     }

     //为了避免混淆 在此显式实现非泛型的接口

     IEnumerator IEnumerable.GetEnumerator()

     {

       return new Enumerator<T>(array); //这步需要重视

     }

 

     //定义一个嵌套类来继承IEnumerator<T>的接口

     //IEnumerator<T>接口继承自IDisposable和IEnumerator接口

     //该接口的唯一成员是Current属性 但是同时也要实现其非泛型版本!!!

     //另外还需要实现IDisposable的Dispose方法和IEnumerator的两个方法

     public class Enumerator<_T> : IEnumerator<_T>

     {

       private int flag;

       private _T[] elements = null ;

       public Enumerator(_T[] items)

       {

         elements = items;

         flag = -1;

       }

       //实现IEnumerator<T>接口的Current属性; 此属性返回集合的当前元素,是只读的

       public _T Current

       {

         get

         {

           if (flag > elements.Length – 1) throw new InvalidOperationException( "枚举已经结束" );

           else if (flag < 0) throw new InvalidOperationException( "枚举尚未开始" );

           else return elements[flag];

         }

       }

       //为了避免混淆  显示实现IEnumerator接口的Current属性

       object IEnumerator.Current

       {

         get { return Current; } //直接返回上面的泛型属性 比较经典

       }

 

       //实现IDisposable接口的Dispose方法 支持确定性垃圾回收 将枚举数的状态设置为after 也就是把标记位设为最大索引+1

       public void Dispose()

       {

         flag = elements.Length + 1;

       }

 

       //实现IEnumerator接口的MoveNext方法 将枚举移动到下一位

       public bool MoveNext()

       {

         ++flag;

         if (flag > (elements.Length – 1)) return false ;

         else return true ;

       }

       //实现IEnumerator接口的Reset方法 使枚举回到开头

       public void Reset()

       {

         flag = -1;

       }

     }

怎么使用呢:

?

SimpleCollection< string > colletion = new SimpleCollection< string >( new string [] { "ranran" , "Huaran" });

    IEnumerator< string > enumorator = colletion.GetEnumerator();

    while (enumorator.MoveNext())

    {

      Console.WriteLine(enumorator.Current);

    }

    foreach (var v in colletion)

    {

      Console.WriteLine(v);

    }

    Console.ReadKey();

还可以直接使用迭代器:
使用迭代器是另一种完全实现上面两个接口的方案,这是最为简便和可读的方法

而且使用迭代器可以很方便和快捷的设置各种枚举情况 如双重的迭代 反向的迭代 临时的集合和负责迭代等等 比上面的实现更为简单

迭代的关键字是yield 需要依靠一个迭代器块(注意是循环+yield  return,或者 yiled break)

?

public class MyCollection:IEnumerable

   {

     private object [] array;

     public MyCollection( object []items)

     {

       array = items;

     }

     public IEnumerator GetEnumerator() //实现都可以依靠编译器去完成

     {

       //foreach (object v in array)

       //{

       //  yield return v;

       //}

 

       //关键字是yield 并不是foreach 我们也可以按照下面这个方法进行实现

       for ( int i=0;i<array.Length;i++)

       {

         yield return array[i];

       }

       //当然其它的while循环也可以。。

     }

   }

//实现:

MyCollection collection = new MyCollection( new object [] { 1, 2, 3 });

       foreach (var v in collection)

       {

         Console.WriteLine(v);

       }

可以自己设置迭代的情况:

 

?

    public class MyCollection2:IEnumerable

   {

      private object [] array;

      public MyCollection2( object []items)

     {

       array = items;

     }

     //可以在迭代器块中设置迭代的实现情况 即具体迭代多少个元素

     //比如我们只想迭代4个元素

     public IEnumerator GetEnumerator()

      {

        int count = 0; //设计一个标记位

       foreach ( object item in array)

       {

         ++count;

         yield return item;

         if (count == 4) yield break ; //break关键字 退出迭代 实际上迭代在实现当中就是一个循环 利用break跳出也合情合理

       }

      }

   

   }

 

 

//////

  MyCollection2 collection2 = new MyCollection2( new object []{4,5,6,7,8});

       //它就只会输出4,5,6,7

       foreach (var v in collection2)

       {

         Console.WriteLine(v);

       }

双重迭代:

?

/// <summary>

   /// 下面演示双重迭代 即一次可以迭代两个集合中的元素

   /// </summary>

   public class MyColletion3:IEnumerable

   {

     private object [] List1;

     public string [] List2;

     public MyColletion3( object []items1, string []items2)

     {

       this .List1 = items1;

       this .List2 = items2;

     }

     //下面进行双重迭代

     public IEnumerator GetEnumerator()

     {

       //关键代码

       for ( int index=0;index<(List1.Length>List2.Length?List2.Length:List1.Length);index++)

       {

         yield return List1[index];

         yield return List2[index];

       }

     }

   }

////////

  MyColletion3 collection3 = new MyColletion3( new object [] { 1, 2, 3, 5.5 }, new string [] { "RanRan" , "Chengdu" , "四川" });

       foreach (var v in collection3)

       {

         Console.WriteLine(v);

       }

       //迭代结果是1 RanRan 2 Chengdu 3 四川

反向迭代:依靠Reverse属性

?

   /// <summary>

   /// 下面演示反向迭代 说白了就是迭代是从后面开始的 反向迭代器是在Reverse属性当中实现的

   /// </summary>

   public class MyColletion4:IEnumerable

   {

     private object [] items;

     public MyColletion4( object []temps)

     {

       this .items = temps;

     }

     //一般的正向迭代

     public IEnumerator GetEnumerator()

     {

       for ( int index=0;index<items.Length;index++)

       {

         yield return items[index];

       }

     }

     //实现反向迭代

     public IEnumerable Reverse //注意返回IEnumerable对象

     {

       get

       {

         for ( int index = items.Length – 1; index > -1; index–)

         {

           yield return items[index];

         }

       }

     }

   }

////

  MyColletion4 collection4 = new MyColletion4( new object [] { 1, 2, 3, 4 });

       foreach (var v in collection4)

       {

         Console.WriteLine(v);

       }

       //反向迭代

       foreach (var v in collection4.Reverse)

       {

         Console.WriteLine(v);

       }

       //迭代结果是 4 3 2 1

当然也有一个临时集合,顺便补充一下,迭代和枚举实现的方案很多,一个返回IEnumerable的方法中加上迭代器块也是一个迭代集合

具体看下面的代码

 

?

//还有一种最为简单的迭代 就是一个返回IEnumerable对象的方法 在这方法中写上迭代器

     //在此补充一个临时集合 关键看代码怎么写(以枚举当前月份的日期为列子)

     public static IEnumerable GetMonthDate()

     {

       DateTime dt = DateTime.Now;

       int currentMonth = dt.Month;

       while (currentMonth==dt.Month)

       {

         string temp = currentMonth.ToString() + "/" + dt.Day.ToString();

         dt = dt.AddDays(1);

         yield return temp;

       }

     }

 

///实现

foreach (var v in GetMonthDate())

       {

         Console.WriteLine(v);

       }

这儿 我作为一个新手自己给自己总结一下可枚举类型和接口的含义:

可枚举类型(集合&数组等):

              在实际开发当中,可以自己去定义一些与集合差不多的类型,对该类型的元素的访问,用一般的while,for循环比较不方便,我们需要自己去定义一个枚举器。

              枚举类型(继承IEnumerable接口):包括一个集合元素和一个枚举器。

              枚举器是枚举类型当中的一个嵌套类(继承了IEnumerator接口):具体实现见上。

              /////// 这样便可以让自定义的可枚举类型实现foreach迭代。

              当然也可以直接利用迭代来实现上面两个接口。//////

接口:是一种标准,它给出了一种约束和引导,需要我们去写代码实现它。虽然看上去多次一举,不过在后面对类的实例的使用中非常方便。

 以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。

原文链接:http://www.cnblogs.com/Huaran1chendu/p/4838536.html

dy(“nrwz”);

赞(0)
版权声明:本文采用知识共享 署名4.0国际许可协议 [BY-NC-SA] 进行授权
文章名称:《C#中可枚举类型详解》
文章链接:https://www.admin99.cn/7451
本站资源仅供个人学习交流,请于下载后24小时内删除,不允许用于商业用途,否则法律问题自行承担。
QQ站长交流群:953950264

登录

找回密码

注册