IEnumerable與IEnumerator在C#中的應用--總結的很好。

 一、示例:

  在C#中,凡是實現了IEnumerator接口的數據類型都可以用foreach語句進行迭代訪問,可是,對於自定義類型如何實現這個接口以支持foreach的迭代呢?

  * 要實現這個功能,先來看看IEnumerableIEnumerator接口的定義:


public interface IEnumerable
  {
  //IEnumerable只有一個方法,返回可循環訪問集合的枚舉數。
  IEnumerator GetEnumerator() ;
  }
  public interface IEnumerator
  {
  // 方法
  //移到集合的下一個元素。如果成功則返回爲 true;如果超過集合結尾,則返回false
  bool MoveNext();
  // 將集合設置爲初始位置,該位置位於集合中第一個元素之前
  void Reset();
  // 屬性:獲取集合中的當前元素
  object Current { get; }
  }
  namespace IEnumberableTest
  {
  class Program
  {
  static void Main(string[] args)
  {
  myEnum enum1 = new myEnum(20);
  foreach (point p in enum1)
  {
  Console.WriteLine("(" + p.x.ToString() + "," + p.y.ToString() + "," + p.z.ToString() + ")");
  }
  Console.Read();
  }
  }
  //一個結構體,用於類myEnum
  struct point
  {
  public int x;
  public int y;
  public int z;
  }
  //一個派生於IEnumerableIEnumerator接口的自定義類
  class myEnum : IEnumerable, IEnumerator
  {
  //定義索引
  private int index;
  //定義一個point結構的數組
  private point[] points;
  //類的構造函數,用於初始化point結構數組
  public myEnum(int numofpoint)
  {
  this.index = -1;
  points = new point[numofpoint];
  for (int i = 0; i < points.Length; i++)
  {
  points[i].x = i;
  points[i].y = i * i;
  points[i].z = i * i * i;
  }
  }
  //實現IEnumerable接口的GetEnumerator方法,返回一個IEnumerator,這裏返回我們的自定義類,因爲要對這個類的對象進行迭代
  public IEnumerator GetEnumerator()
  {
  return (IEnumerator)this;
  }
  //實現IEnumerator接口的Reset方法,將集合索引置於第一個元素之前
  public void Reset()
  {
  index = -1;
  }
  //實現IEnumerator接口的Current屬性,返回一個自定義的point結構,即point數組的第index元素
  public object Current
  {
  get
  {
  return points[index];
  }
  }
  //實現IEnumerator接口的MoveNext方法,用於向前訪問集合元素,如果超出集合範圍,返回false
  public bool MoveNext()
  {
  index++;
  return index >= points.Length ? false : true;
  }
  }
  }

 

二、區別:

  IEnumerableIEnumerator有什麼區別?這是一個很讓人困惑的問題(在很多forum裏都看到有人在問這個問題)。研究了半天,得到以下幾點認識:

  1、一個Collection要支持foreach方式的遍歷,必須實現IEnumerable接口(亦即,必須以某種方式返回IEnumerator object)

  2IEnumerator object具體實現了iterator(通過MoveNext()Reset()Current)

  3、從這兩個接口的用詞選擇上,也可以看出其不同:IEnumerable是一個聲明式的接口,聲明實現該接口的class可枚舉(enumerable)”的,但並沒有說明如何實現枚舉器(iterator);IEnumerator是一個實現式的接口,IEnumerator object就是一個iterator

  4IEnumerableIEnumerator通過IEnumerableGetEnumerator()方法建立了連接,client可以通過IEnumerableGetEnumerator()得到IEnumerator object,在這個意義上,將GetEnumerator()看作IEnumerator objectfactory method也未嘗不可。

  IEnumerator 是所有枚舉數的基接口。

  枚舉數只允許讀取集合中的數據。枚舉數無法用於修改基礎集合。

  最初,枚舉數被定位於集合中第一個元素的前面。Reset 也將枚舉數返回到此位置。在此位置,調用 Current 會引發異常。因此,在讀取 Current 的值之前,必須調用 MoveNext 將枚舉數提前到集合的第一個元素。

  在調用 MoveNext Reset 之前,Current 返回同一對象。MoveNext Current 設置爲下一個元素。

  在傳遞到集合的末尾之後,枚舉數放在集合中最後一個元素後面,且調用 MoveNext 會返回 false。如果最後一次調用 MoveNext 返回 false,則調用 Current 會引發異常。若要再次將 Current 設置爲集合的第一個元素,可以調用 Reset,然後再調用 MoveNext

  只要集合保持不變,枚舉數就將保持有效。如果對集合進行了更改(例如添加、修改或刪除元素),則該枚舉數將失效且不可恢復,並且下一次對 MoveNext Reset 的調用將引發 InvalidOperationException。如果在 MoveNext Current 之間修改集合,那麼即使枚舉數已經無效,Current 也將返回它所設置成的元素。

  枚舉數沒有對集合的獨佔訪問權;因此,枚舉一個集合在本質上不是一個線程安全的過程。甚至在對集合進行同步處理時,其他線程仍可以修改該集合,這會導致枚舉數引發異常。若要在枚舉過程中保證線程安全,可以在整個枚舉過程中鎖定集合,或者捕捉由於其他線程進行的更改而引發的異常。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章