C#基础知识系列九(对IEnumerable和IEnumerator接口的糊涂认识)

简介: 前言    IEnumerable、IEnumerator到现在为止对这两个接口还是不太理解,不理解但是自己总是想着试着要搞明白,毕竟自己用的少,所以在此先记录一下。以备自己日后可以来翻查,同时也希望园子里的大牛们,来帮我看看理解的怎么样。

前言

   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关键字的用法。  

目录
相关文章
|
达摩院 Linux API
阿里达摩院MindOpt求解器V1.1新增C#接口
阿里达摩院MindOpt求解器发布最新版本V1.1,增加了C#相关API和文档。优化求解器产品是求解优化问题的专业计算软件,可广泛各个行业。阿里达摩院从2019年投入自研MindOpt优化求解器,截止目前经历27个版本的迭代,取得了多项国内和国际第一的成绩。就在上个月,2023年12月,在工信部产业发展促进中心等单位主办的首届能源电子产业创新大赛上,MindOpt获得电力用国产求解器第一名。本文将为C#开发者讲述如何下载安装MindOpt和C#案例源代码。
524 3
阿里达摩院MindOpt求解器V1.1新增C#接口
|
编译器 C# 开发者
C# 11.0中的新特性:覆盖默认接口方法
C# 11.0进一步增强了接口的灵活性,引入了覆盖默认接口方法的能力。这一新特性允许类在实现接口时,不仅可以提供接口中未实现的方法的具体实现,还可以覆盖接口中定义的默认方法实现。本文将详细介绍C# 11.0中接口默认方法覆盖的工作原理、使用场景及其对现有代码的影响,帮助开发者更好地理解和应用这一新功能。
|
IDE C# 开发工具
C#系列之接口介绍
C#系列之接口介绍
174 0
|
安全 C# 开发者
C#中的默认接口方法:接口演化的新篇章
【1月更文挑战第11天】本文探讨了C# 8.0中引入的默认接口方法,这一特性允许在接口中定义具有默认实现的方法。文章介绍了默认接口方法的语法、使用场景,以及它们如何影响接口的设计和实现,同时讨论了默认接口方法带来的好处和潜在的陷阱。
|
11月前
|
开发框架 监控 .NET
C#进阶-ASP.NET WebForms调用ASMX的WebService接口
通过本文的介绍,希望您能深入理解并掌握ASP.NET WebForms中调用ASMX WebService接口的方法和技巧,并在实际项目中灵活运用这些技术,提高开发效率和应用性能。
664 5
C# 接口(Interface)
接口定义了所有类继承接口时应遵循的语法合同。接口定义了语法合同 "是什么" 部分,派生类定义了语法合同 "怎么做" 部分。 接口定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明。成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。 接口使得实现接口的类或结构在形式上保持一致。 抽象类在某种程度上与接口类似,但是,它们大多只是用在当只有少数方法由基类声明由派生类实现时。 接口本身并不实现任何功能,它只是和声明实现该接口的对象订立一个必须实现哪些行为的契约。 抽象类不能直接实例化,但允许派生出具体的,具有实际功能的类。
157 9
|
C# 索引
C# 一分钟浅谈:接口与抽象类的区别及使用
【9月更文挑战第2天】本文详细对比了面向对象编程中接口与抽象类的概念及区别。接口定义了行为规范,强制实现类提供具体实现;抽象类则既能定义抽象方法也能提供具体实现。文章通过具体示例介绍了如何使用接口和抽象类,并探讨了其实现方式、继承限制及实例化差异。最后总结了选择接口或抽象类应基于具体设计需求。掌握这两者有助于编写高质量的面向对象程序。
681 5
|
API C# 数据库
SemanticKernel/C#:实现接口,接入本地嵌入模型
SemanticKernel/C#:实现接口,接入本地嵌入模型
257 1
|
设计模式 自然语言处理 C#
C#OOP之八 继承 接口和抽象类
C#OOP之八 继承 接口和抽象类
115 0
|
前端开发 API C#
C# 接口
C# 接口
99 1

热门文章

最新文章

下一篇