IList,ICollection,IEnumerable,IEnumerator,IQueryable
http://www.cnblogs.com/edison1105/archive/2012/07/30/2616082.html
1、首先看一個簡單的例子
int[] myArray = { 1, 32, 43, 343 }; IEnumerator myie = myArray.GetEnumerator(); myie.Reset(); while (myie.MoveNext()) { int i = (int)myie.Current; Console.WriteLine("Value: {0}", i); }
相信很多人都不會像上面這樣去遍歷myArray這個數組,通常我們這樣會這樣做
foreach (int item in myArray) Console.WriteLine(item.ToString());
在大部分的情況下,很多人會使用for和foreach來遍歷數組,而對於上面的語法卻用的很少,但是對foreach的具體來歷還很模糊!
2、理解foreach
大家都知道要實現foreach的必須要實現IEnumerable和IEnumerator的接口,只有實現了它們,才能實現遍歷,所以要講foreach的來歷,必須要把那兩個接口給搞清楚點!
這邊也想說明一點的是:如果對這兩個接口有了一定的了解後,只要實現那個GetEnumerator方法即可,而不需要實現於IEnumerable接口,這只是針對對這兩個接口有一定了解的朋友!
2.1 IEnumerable
具體的作用:就是使實現這個接口的對象成為可枚舉類型。
IEnumerable接口包含一個GetEnumerator方法,返回值為IEnumerator的類型!代碼如下:
public class MyColors : IEnumerable { string[] colors = { "Red", "Yellow", "Biue"}; public IEnumerator GetEnumerator() { return colors.GetEnumerator(); } }那麽我們在客戶端進行調用的時候就可以這樣做了!
MyColors colors = new MyColors(); foreach (string item in colors) Console.WriteLine(item);這樣就能使用實現這個接口的對象進行foreach遍歷了,就是這麽簡單的一個列子,這邊可能有的人會有疑問,我們就實現了IEnumerable接口但卻沒實現IEnumerator接口,可是我們不是說只有實現了這兩個接口才可以進行foreach遍歷嗎?可以這樣說當使用forach進行遍歷的時候,編譯器會到這個對象的類中去尋找GetEnumerator方法,找到這個方法後返回這個一個IEnumerator的對象(枚舉數),而我這邊返回的是“colors.GetEnumerator()”,是一個數組對象調用它本身中的“GetEnumerator”方法,所以說數組本身就實現了IEnumerator接口,那麽兩個接口都實現了,不就好實現foreach遍歷了,其實在實現遍歷枚舉數的時候編譯器會自動去調用數組中實現枚舉數的類中的方法。
2.2 IEnumerator
接口的作用:實現可枚舉數,首先看一下接口的定義:
包含一個屬性兩個方法
MoveNext → 把當前的項移動到下一項(類似於索引值),返回一個bool值,這個bool值用來檢查當前項是否超出了枚舉數的範圍!
Current → 獲取當前項的值,返回一個object的類型(這邊會涉及到裝箱和拆箱的問題 → 後面講泛型的時候會涉及到)!
Reset → 顧名思義也就是把一些值恢復為默認值,比如把當前項恢復到默認狀態值!
public class MyIEnumerator : IEnumerator { public MyIEnumerator(string[] colors) { this.colors = new string[colors.Length]; for (int i = 0; i < this.colors.Length; i++) this.colors[i] = colors[i]; } string[] colors; //定義一個數組,用來存儲數據 int position = -1; //定義當前項的默認值,也就是索引值,一開始認識數組的索引從“0”開始, //怎麽默認設置他為“-1”呢,最後才想明白,這樣設置是合情合理的! public object Current //根據當前項獲取相應的值 { get { return colors[position]; //返回當前項的值,但是會做一個裝箱的操作! } } public bool MoveNext() //移動到下一項 { if (position < colors.Length - 1) //這就是設置默認值為-1的根據 { position++; return true; } else { return false; } } //重置當前項的值,恢復為默認值 public void Reset() { this.position = -1; } }上面講到的IEnumerable接口中GetEnumerator方法是獲取要遍歷的枚舉數,在我們沒有創建自己的遍歷枚舉數的類時,我們使用的是Array的遍歷枚舉數的方法(關於數組的可枚舉類型和枚舉數會在下一篇講到),但這個有的時候不一定適合我們,我們需要為自己定制一個更合適的,所以我們要創建自己的枚舉數類(也就是上面的代碼),把第三點和第四點的代碼合並起來(改動一點代碼),如下:
public class MyColors //: IEnumerable { string[] colors = { "Red", "Yellow", "Biue" }; public IEnumerator GetEnumerator() { return new MyIEnumerator(colors); } }
3、關於可枚舉類型和枚舉數
①可枚舉類型 → 實現IEnumerable接口,可以不需要直接實現這個接口,但必須有個GetEnumerator方法,返回值類型必須為IEnumerator類型,也就是第四點最後一段代碼中接口註釋的那種寫法!
②枚舉數 → 實現IEnumerator接口,實現全部方法,首先是調用GetEnumerator返回一個類型為IEnumerator的枚舉數,然後編譯器會隱式的調用實現IEnumerator類中的方法和屬性!
總結:所以實現foreach遍歷,必須達到上面的兩種條件才能進行遍歷對象,他們可以寫在一起也可以分開,最好是分開,進行職責分離,一個類幹一件事總歸是好事!也滿足面向對象的單一指責設計原則。
下面的代碼示例演示如何實現自定義集合的 IEnumerable 和 IEnumerator 接口(代碼來自MSDN)
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication1 { public class Person { public Person(string fName, string lName) { this.firstName = fName; this.lastName = lName; } public string firstName; public string lastName; } public class People : IEnumerable { private Person[] _people; public People(Person[] pArray) { _people = new Person[pArray.Length]; for (int i = 0; i < pArray.Length; i++) { _people[i] = pArray[i]; } } IEnumerator IEnumerable.GetEnumerator() { return (IEnumerator)GetEnumerator(); } public PeopleEnum GetEnumerator() { return new PeopleEnum(_people); } } public class PeopleEnum : IEnumerator { public Person[] _people; // Enumerators are positioned before the first element // until the first MoveNext() call. int position = -1; public PeopleEnum(Person[] list) { _people = list; } public bool MoveNext() { position++; return (position < _people.Length); } public void Reset() { position = -1; } object IEnumerator.Current { get { return Current; } } public Person Current { get { try { return _people[position]; } catch (IndexOutOfRangeException) { throw new InvalidOperationException(); } } } } class Program { static void Main(string[] args) { Person[] peopleArray = new Person[3] { new Person("John", "Smith"), new Person("Jim", "Johnson"), new Person("Sue", "Rabon"), }; People peopleList = new People(peopleArray); foreach (Person p in peopleList) Console.WriteLine(p.firstName + " " + p.lastName); } } }
無圖無真相
我並非標題黨,下面介紹IList,ICollection,IQueryable
1、IList 是 ICollection 接口的子代,並且是所有非泛型列表的基接口。IList 實現有三種類別:只讀、固定大小和可變大小。無法修改只讀 IList。固定大小的 IList 不允許添加或移除元素,但允許修改現有元素。可變大小的 IList 允許添加、移除和修改元素。
2、ICollection 接口是 System.Collections 命名空間中類的基接口。ICollection 接口擴展 IEnumerable;IDictionary 和 IList 則是擴展 ICollection 的更為專用的接口。 IDictionary 實現是鍵/值對的集合,如 Hashtable 類。 IList 實現是值的集合,其成員可通過索引訪問,如 ArrayList 類。 某些集合(如 Queue 類和 Stack 類)限制對其元素的訪問,它們直接實現 ICollection 接口。 如果 IDictionary 接口和 IList 接口都不能滿足所需集合的要求,則從 ICollection 接口派生新集合類以提高靈活性。定義所有非泛型集合的大小、枚舉器和同步方法。
3、IQueryable 提供對未指定數據類型的特定數據源的查詢進行計算的功能,IQueryable 接口由查詢提供程序實現。 該接口只能由同時實現 IQueryable(Of T) 的提供程序實現。 如果該提供程序不實現 IQueryable(Of T),則無法對提供程序數據源使用標準查詢運算符。 IQueryable 接口繼承 IEnumerable 接口,以便在前者表示一個查詢時可以枚舉該查詢的結果。 枚舉強制執行與 IQueryable 對象關聯的表達式樹。 “執行表達式樹”的定義是查詢提供程序所特有的。 例如,它可能涉及將表達式樹轉換為適用於基礎數據源的查詢語言。 在調用 Execute 方法時將執行不返回可枚舉結果的查詢。
不好意思,這裏就先粗略的介紹一下了,都說我很懶了,明天還要上班,下次再續…
IList,ICollection,IEnumerable,IEnumerator,IQueryable