1. 程式人生 > >C#中IEnumerable、ICollection、IList、IQueryable 、List之間的區別

C#中IEnumerable、ICollection、IList、IQueryable 、List之間的區別

一:一個簡單的例子

1

2

3

4

5

6

7

8

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);

            }

 通常我們這樣會這樣做:

1

2

foreach (int item in myArray)

 Console.WriteLine(item.ToString());

 使用for和foreach來遍歷陣列,而對於上面的語法卻用的很少,但是對foreach的具體來歷還很模糊!】

二:理解Foreach

要實現foreach的必須要實現IEnumerable和IEnumerator的介面,只有實現了它們,才能實現遍歷,所以要講foreach的來歷,必須要把那兩個介面給搞清楚點!

如果對這兩個介面有了一定的瞭解後,只要實現那個GetEnumerator方法即可,而不需要實現於IEnumerable介面

   1.IEnumerable

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

具體的作用:就是使實現這個介面的物件成為可列舉型別。

 

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);

 

 陣列本身就實現了IEnumerator介面,那麼兩個介面都實現了,不就好實現foreach遍歷了,其實在實現遍歷列舉數的時候編譯器會自動去呼叫陣列中實現列舉數的類中的方法。

 

  2.IEnumerator:

介面的作用:實現可列舉數,首先看一下介面的定義:

包含一個屬性兩個方法

MoveNext → 把當前的項移動到下一項(類似於索引值),返回一個bool值,這個bool值用來檢查當前項是否超出了列舉數的範圍!

Current → 獲取當前項的值,返回一個object的型別

Reset → 顧名思義也就是把一些值恢復為預設值,比如把當前項恢復到預設狀態值!

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

    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”開始,

       

 

        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的遍歷列舉數的方法,<br>但這個有的時候不一定適合我們,我們需要為自己定製一個更合適的,所以我們要建立自己的列舉數類(也就是上面的程式碼),把第三點和第四點的程式碼合併起來(改動一點程式碼),如下:

 

        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 介面

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

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);

        }

    }

}

 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 方法時將執行不返回可列舉結果的查詢。

 

 

 

 

IQueryable和IEnumberable and IList與Lis t區別

基本概念:

IEnumerable:使用的是LINQ to Object方式,它會將AsEnumerable()時對應的所有記錄都先載入到記憶體,然後在此基礎上再執行後來的Query

IQeurable(IQuerable<T>):不在記憶體載入持久資料,因為這傢伙只是在組裝SQL,(延遲執行) 到你要使用的時候,
例如  list.Tolist() or list.Count()的時候,資料才從資料庫進行載入 (AsQueryable())。

IList(IList<T>):泛型介面是 ICollection 泛型介面的子代,作為所有泛型列表的基介面,
在用途方面如果作為資料集合的載體這是莫有問題的,只是如果需要對集合做各種的操作,例如 排序 編輯 統計等等,它不行。

List <> :泛型類,它已經實現了IList <> 定義的那些方法,IList<T> list=new List<T>();
只是想建立一個基於介面IList<Class1>的物件的例項,這個介面是由List<T>實現的。只是希望使用到IList<T>介面規定的功能而已  


抽象場景:
其實在我們之前沒有使用 ORM 的的很久很久以前,我們 在ADO.net 裡面使用的 DataReader 和 DataAdapter or DataSet 和這幾個貨的基本原理都接近的,
就是讀取資料的時候,一個必須獨佔著資料庫的連線,而另一個就是先把資料庫的的局載入到了自己本地,然後再進行操作。


使用場景模擬:

1

2

3

4

5

6

7

8

9

10

複製程式碼

//IList

IList users = res.ToList(); //此時已把users載入到記憶體,而每個user的關聯實體(UserInfos)未

                                       //被載入,所以下一行程式碼無法順利通過

var ss = users.Where(p => p.UserInfos.ID != 3); //此處報錯,因為P的UserInfos實體無法被載入

  

// IQuerable的

IQueryable users = res.AsQueryable(); //users未被立即載入,關聯實體可通過“延遲載入”獲

                                   //得

var ss = users.Where(p => p.UserInfos.ID != 3);//此處順利獲得對應的ss

 

總結:

基於效能和資料一致性這兩點,使用IQueryable時必須謹慎,而在大多數情況下我們應使用IList。

1.當你打算馬上使用查詢後的結果(比如迴圈作邏輯處理或者填充到一個table/grid中),

並且你不介意該查詢即時被執行後的結果可以供呼叫者(Consummer)作後續查詢(比如這是一個"GetAll"的方法),或者你希望該查執行,使用ToList()
2.當你希望查詢後的結果可以供呼叫者(Consummer)作後續查詢(比如這是一個"GetAll"的方法),或者你希望該查詢延時執行,使用AsQueryable()
3.按照功能由低到高:List<T> IList<T> IQueryable<T> IEnumerable<T>
4.按照效能由低到高:IEnumerable<T> IQueryable<T> IList<T> List<T>

轉自:https://www.cnblogs.com/sunliyuan/p/5823419.html