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()时对应的所有记录都先加载到内存,然后在此基础上再执行后来的QueryIQeurable(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

这篇关于C#中IEnumerable、ICollection、IList、IQueryable 、List之间的区别的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/731316

相关文章

Springboot @Autowired和@Resource的区别解析

《Springboot@Autowired和@Resource的区别解析》@Resource是JDK提供的注解,只是Spring在实现上提供了这个注解的功能支持,本文给大家介绍Springboot@... 目录【一】定义【1】@Autowired【2】@Resource【二】区别【1】包含的属性不同【2】@

使用C#代码在PDF文档中添加、删除和替换图片

《使用C#代码在PDF文档中添加、删除和替换图片》在当今数字化文档处理场景中,动态操作PDF文档中的图像已成为企业级应用开发的核心需求之一,本文将介绍如何在.NET平台使用C#代码在PDF文档中添加、... 目录引言用C#添加图片到PDF文档用C#删除PDF文档中的图片用C#替换PDF文档中的图片引言在当

详解C#如何提取PDF文档中的图片

《详解C#如何提取PDF文档中的图片》提取图片可以将这些图像资源进行单独保存,方便后续在不同的项目中使用,下面我们就来看看如何使用C#通过代码从PDF文档中提取图片吧... 当 PDF 文件中包含有价值的图片,如艺术画作、设计素材、报告图表等,提取图片可以将这些图像资源进行单独保存,方便后续在不同的项目中使

Java中的String.valueOf()和toString()方法区别小结

《Java中的String.valueOf()和toString()方法区别小结》字符串操作是开发者日常编程任务中不可或缺的一部分,转换为字符串是一种常见需求,其中最常见的就是String.value... 目录String.valueOf()方法方法定义方法实现使用示例使用场景toString()方法方法

Java中List的contains()方法的使用小结

《Java中List的contains()方法的使用小结》List的contains()方法用于检查列表中是否包含指定的元素,借助equals()方法进行判断,下面就来介绍Java中List的c... 目录详细展开1. 方法签名2. 工作原理3. 使用示例4. 注意事项总结结论:List 的 contain

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

C#数据结构之字符串(string)详解

《C#数据结构之字符串(string)详解》:本文主要介绍C#数据结构之字符串(string),具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录转义字符序列字符串的创建字符串的声明null字符串与空字符串重复单字符字符串的构造字符串的属性和常用方法属性常用方法总结摘

C#如何动态创建Label,及动态label事件

《C#如何动态创建Label,及动态label事件》:本文主要介绍C#如何动态创建Label,及动态label事件,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C#如何动态创建Label,及动态label事件第一点:switch中的生成我们的label事件接着,

C# WinForms存储过程操作数据库的实例讲解

《C#WinForms存储过程操作数据库的实例讲解》:本文主要介绍C#WinForms存储过程操作数据库的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、存储过程基础二、C# 调用流程1. 数据库连接配置2. 执行存储过程(增删改)3. 查询数据三、事务处

分辨率三兄弟LPI、DPI 和 PPI有什么区别? 搞清分辨率的那些事儿

《分辨率三兄弟LPI、DPI和PPI有什么区别?搞清分辨率的那些事儿》分辨率这个东西,真的是让人又爱又恨,为了搞清楚它,我可是翻阅了不少资料,最后发现“小7的背包”的解释最让我茅塞顿开,于是,我... 在谈到分辨率时,我们经常会遇到三个相似的缩写:PPI、DPI 和 LPI。虽然它们看起来差不多,但实际应用