IEnumerable、IEnumerator到現在為止對這兩個接口還是不太理解,不理解但是自己總是想著試著要搞明白,畢竟自己用的少,所以在此先記錄一下。以備自己日後可以來翻查,同時也希望園子裡的大牛們,來幫我看看理解的怎麼樣。
接下來我們先來看看兩個接口的定義。
先來看一下IEnumerable接口,其實看過這個接口之後,發現它其實是非常的簡單,只包含一個方法GetEnumerator(),它返回一個可用於循環訪問集合的IEnumerator對象,如下面截圖所示:
這裡的IEnumerator對象,其實就是另外一個接口,這個接口對象有什麼呢?它是一個真正的集合訪問器,沒有它,就不能使用foreach語句遍歷集合或數組,因為只有IEnumerator對象才能訪問集合中的項,假如連集合中的項都訪問不了,那麼進行集合的循環遍歷是不可能的事情了。那麼讓我們看看IEnumerator接口又定義了什麼東西。
從上面我們知道IEnumerator接口定義了一個Current屬性,MoveNext和Reset兩個方法,這是多麼的簡約。既然IEnumerator對象是一個訪問器。那至少應該有一個Current屬性,來獲取當前集合中的項吧。MoveNext方法只是將游標的內部位置向前移動(就是移到一下個元素而已),要想進行循環遍歷,不向前移動一下怎麼行呢?
通過注釋也可以明確的發現他們的用處。
下面我們來看一個簡單的例子:
static void Main(string[] args) { int[] iArr = { 1, 3, 4, 6, 7, 9 }; foreach (int i in iArr) { Console.WriteLine(i.ToString()); } Console.ReadLine(); }
F5來運行代碼
結果有了,說明簡單的數組是可以支持foreach循環的。
下面我們來自己來做一個小例子,先來定義實體類
/// <summary> /// 個人的實體類 /// </summary> public class Person { public string Name { get; set; } public int Age { get; set; } } /// <summary> /// 一群人的實體類 /// </summary> public class People { Person[] personList = new Person[4]; public People() { personList[0] = new Person() { Name = "aehyok", Age = 25 }; personList[1] = new Person() { Name = "Kris", Age = 22 }; personList[2] = new Person() { Name = "Leo", Age = 21 }; personList[3] = new Person() { Name = "Niki", Age = 23 }; } }
如上面代碼所示,一個Person類是個人的實體類,然後People類是一群人的實體類,按照和上面數組類似的格式,下面我們進行調用
static void Main(string[] args) {
///直接對一群人實例對象進行foreach People people = new People(); foreach (Person p in people) { Console.WriteLine("Name:{0}\tAge{1}",p.Name,p.Age); } Console.ReadLine(); }
還沒來得及編譯,錯誤就來了
所以我們根據上面的講解我們就讓People類實現IEnumerable接口吧。現在先來修改People實體類。
/// <summary> /// 個人的實體類 /// </summary> public class Person { public string Name { get; set; } public int Age { get; set; } } /// <summary> /// 一群人的實體類 /// </summary> public class People:IEnumerable { Person[] personList = new Person[4]; public People() { personList[0] = new Person() { Name = "aehyok", Age = 25 }; personList[1] = new Person() { Name = "Kris", Age = 22 }; personList[2] = new Person() { Name = "Leo", Age = 21 }; personList[3] = new Person() { Name = "Niki", Age = 23 }; } public IEnumerator GetEnumerator() { return this.personList.GetEnumerator(); } }
繼承實現接口,完成該方法之後,就可以在調用時用foreach了。
注意:其實這裡完全不用繼承該接口。直接對GetEnumerator()方法進行實現,然後返回IEnumerator即可。
這樣還可以有另外一種調用方式
static void Main(string[] args) { People people = new People(); foreach (Person p in people) { Console.WriteLine("Name:{0}\tAge{1}",p.Name,p.Age); } Console.WriteLine(""); ///直接獲取迭代器 IEnumerator i = people.GetEnumerator(); while (i.MoveNext()) { Person person = (Person)i.Current; Console.WriteLine("Name:{0}\tAge{1}", person.Name, person.Age); } Console.ReadLine(); }
調用結果
上面我們是通過繼承微軟類庫中的接口來實現的實體集合的foreach遍歷。下面我們來演示一下完全通過自己創建接口來實現自己定義的實例集合的foreach遍歷。首先我們來實現一個簡單的迭代器。
第一步:定義一個接口IMyEnumerator,之後所有迭代器都要進行實現
/// <summary> /// 要求所有的迭代器全部實現該接口 /// </summary> interface IMyEnumerator { bool MoveNext(); object Current{get;}; }
第二步:再定義一個接口IMyEnumerable,所有集合要實現該接口
/// <summary> /// 要求所有的集合實現該接口 /// 這樣一來,客戶端就可以針對該接口編碼 /// 而無須關注具體的實現 /// </summary> interface IMyEnumerable { IMyEnumerator GetEnumerator(); int Count{get;}; }
第三步:一個簡單的集合類MyList,實現IMyEnumerable。
class MyList:IMyEnumerable { int[] items = {0,1,2,3,4,5,6,7,8,9}; IMyEnumerator myEnumerator; public int this[int i] { get { return items[i]; } set { this.items[i] = value; } } public int Count { get { return items.Length; } } public IMyEnumerator GetEnumerator() { if (myEnumerator == null) { myEnumerator = new MyEnumerator(this); } return myEnumerator; } }
第四步:其實集合中也需要進行使用實現了第一步的迭代器,所以在此就是要實現這個迭代器
public class MyEnumerator:IMyEnumerator { int index = 0; MyList myList; public MyEnumerator(MyList myList) { this.myList = myList; } public bool MoveNext() { if (index + 1 > =myList.Count) { index = 1; return false; } else { index++; return true; } } public object Current { get { return myList[index]; } } }
第五步:簡單調用進行調試
static void Main(string[] args) { ///使用接口IMyEnumerable代替MyList IMyEnumerable list = new MyList(); ///得到迭代器,在循環中針對迭代器進行編碼,而不是集合MyList IMyEnumerator enumerator = list.GetEnumerator(); for (int i = 0; i < list.Count; i++) { object current = enumerator.Current; Console.WriteLine(current.ToString()); enumerator.MoveNext(); } Console.WriteLine(""); ///重新創建一個新的對象 IMyEnumerable list1 = new MyList(); IMyEnumerator enumerator1 = list1.GetEnumerator(); while (enumerator1.MoveNext()) //因為此處閒下移了一位,所以從1開始 { object current = enumerator1.Current; Console.WriteLine(current.ToString()); } Console.ReadLine(); }
調用結果
其實我定義的兩個接口使用的是IMyEnumerable和IMyEnumerator,這裡你直接可以去掉My那麼就是微軟類庫裡面的接口了,我這裡只是自定義罷了,然後我自己定義接口的方法屬性,沒有嚴格按照微軟的接口進行定義,但是差不多,只需要進行簡單的修正就可以進行調用。這裡有一個版本的。
View Code其實上面例子中的調用我們就可以使用foreach來調用了,那麼現在我們來用foreach來調用看看。
static void Main(string[] args) { MyList list=new MyList(); foreach (object obj in list) { Console.WriteLine(obj.ToString()); } Console.ReadLine(); }
通過上面我實現的幾個簡單的例子可以發現,一個類型支持foreach遍歷的條件可以是:
1、第一個方案是:這個類實現IEnumerable接口
2、第二個方案是:這個類有一個public的GetEnumerator的實例方法(不用繼承IEnumerable實現接口),並且返回類型中有public 的bool MoveNext()實例方法和public的Current實例屬性。
實現了IEnmerable<T>接口的集合,是強類型的。它為子對象的迭代提供類型更加安全的方式。
自己實現了下,感覺還是懂了一些,雖然還沒有徹底的搞明白,但最起碼大概知道怎麼回事了。有空再來看看yield關鍵字的用法。