前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >C#基础知识系列九(对IEnumerable和IEnumerator接口的糊涂认识)

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

作者头像
aehyok
发布于 2018-08-31 02:22:21
发布于 2018-08-31 02:22:21
60900
代码可运行
举报
文章被收录于专栏:技术博客技术博客
运行总次数:0
代码可运行

前言

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

查看并使用两个接口

  接下来我们先来看看两个接口的定义。

  先来看一下IEnumerable接口,其实看过这个接口之后,发现它其实是非常的简单,只包含一个方法GetEnumerator(),它返回一个可用于循环访问集合的IEnumerator对象,如下面截图所示:

这里的IEnumerator对象,其实就是另外一个接口,这个接口对象有什么呢?它是一个真正的集合访问器,没有它,就不能使用foreach语句遍历集合或数组,因为只有IEnumerator对象才能访问集合中的项,假如连集合中的项都访问不了,那么进行集合的循环遍历是不可能的事情了。那么让我们看看IEnumerator接口又定义了什么东西。

从上面我们知道IEnumerator接口定义了一个Current属性,MoveNext和Reset两个方法,这是多么的简约。既然IEnumerator对象是一个访问器。那至少应该有一个Current属性,来获取当前集合中的项吧。MoveNext方法只是将游标的内部位置向前移动(就是移到一下个元素而已),要想进行循环遍历,不向前移动一下怎么行呢?

通过注释也可以明确的发现他们的用处。

下面我们来看一个简单的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        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循环的。

下面我们来自己来做一个小例子,先来定义实体类

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    /// <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类是一群人的实体类,按照和上面数组类似的格式,下面我们进行调用

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        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实体类。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    /// <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即可。

这样还可以有另外一种调用方式

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        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,之后所有迭代器都要进行实现

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    /// <summary>
    /// 要求所有的迭代器全部实现该接口
    /// </summary>
    interface IMyEnumerator
    {
        bool MoveNext();
        object Current{get;};
    }

第二步:再定义一个接口IMyEnumerable,所有集合要实现该接口

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    /// <summary>
    /// 要求所有的集合实现该接口
    /// 这样一来,客户端就可以针对该接口编码
    /// 而无须关注具体的实现
    /// </summary>
    interface IMyEnumerable
    {
        IMyEnumerator GetEnumerator();
        int Count{get;};
    }

第三步:一个简单的集合类MyList,实现IMyEnumerable。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    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;
        }

    }

第四步:其实集合中也需要进行使用实现了第一步的迭代器,所以在此就是要实现这个迭代器

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
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]; }
        }
    }

第五步:简单调用进行调试

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        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那么就是微软类库里面的接口了,我这里只是自定义罢了,然后我自己定义接口的方法属性,没有严格按照微软的接口进行定义,但是差不多,只需要进行简单的修正就可以进行调用。这里有一个版本的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
View Code

其实上面例子中的调用我们就可以使用foreach来调用了,那么现在我们来用foreach来调用看看。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
        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关键字的用法。  

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2014-04-03 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
「持续集成实践系列 」Jenkins 2.x 构建 CI 自动化流水线常见技巧 (二)
在上一篇文章中,我们介绍了Jenkins 2.x实现流水线的两种语法,以及在实际工作中该如何选择脚本式语法或声明式语法。原文可查阅:「持续集成实践系列」Jenkins 2.x 搭建CI需要掌握的硬核要点(一)
测试开发技术
2020/06/05
1.3K0
「持续集成实践系列 」Jenkins 2.x 构建 CI 自动化流水线常见技巧 (二)
打造企业级自动化运维平台系列(六):Jenkins Pipeline 入门及使用详解
作为一种流行的持续集成和交付工具,Jenkins有多种方式来实现交付流水线。其中,Jenkins Pipeline是一种比较流行的方式,它提供了一个DSL(Domain Specific Language 的缩写,中文翻译为:领域特定语言)来描述交付流水线。
民工哥
2024/01/18
2.4K0
打造企业级自动化运维平台系列(六):Jenkins Pipeline 入门及使用详解
自动化集成:Pipeline流水语法详解
Pipeline流水线任务通常情况下都是自动触发的,在Git仓库中配置源码改动后通知的地址即可。
知了一笑
2021/12/24
1.1K0
自动化集成:Pipeline流水语法详解
史诗级整理 Jenkins 流水线详解
jenkins 有 2 种流水线分为声明式流水线与脚本化流水线,脚本化流水线是 jenkins 旧版本使用的流水线脚本,新版本 Jenkins 推荐使用声明式流水线。文档只介绍声明流水线。
我的小碗汤
2023/03/19
8K0
史诗级整理 Jenkins 流水线详解
通过jenkins API去build一个job
背景 查看jenkins的api 直接访问 JENKINS_URL/job/JOB_NAME/api/ 就可以查看jenkins的api build一个job的话,是POST请求 JENKINS_UR
千往
2018/01/24
2.4K0
通过jenkins API去build一个job
Kubernetes 动态创建 Jenkins Agent 压力测试
前面我们利用 Kubernetes 提供的弹性,在 Kubernetes 上动态创建 Jenkins Slave,本文主要是对 Jenkins 进行大规模构建的压力测试。
我是阳明
2020/12/01
1.8K0
Kubernetes 动态创建 Jenkins Agent 压力测试
使用Go案例完成开发到部署实践
我们准备一个apis模块,在该模块中实现一个打印字符串的函数Says(str string):所以
公众号: 云原生生态圈
2020/06/12
8710
【Jenkins系列】-Pipeline语法全集
parameters指令提供用户在触发Pipeline时的参数列表。这些参数值通过该params对象可用于Pipeline步骤
DevOps在路上
2023/05/16
2K0
【Jenkins系列】-Pipeline语法全集
从0搭建属于自己的Jenkins持续集成平台
Jenkins在日常工作中占据了一个非常重要的角色,帮助我们节省了大量用于构建的时间。有些公司有运维大哥对Jenkins进行维护,如果没有那只能自己动手了。俗话说的好自己动手丰衣足食,所以本文就从0开始搭建属于自己的Jenkins持续平台。主要包含,普通项目构建、流水线构建、多分支流水线构建并将构建结果辅以钉钉通知。
不一样的科技宅
2020/07/08
1.1K1
2.Jenkins进阶之流水线pipeline语法入门学习
Q: 什么是 Groovy 语言 答: Groovy 是 Apache 旗下的一门基于 JVM 平台的动态/敏捷编程语言,在语言的设计上它吸纳了 Python、Ruby 和 Smalltalk 语言的优秀特性,语法非常简练和优美,开发效率也非常高(编程语言的开发效率和性能是相互矛盾的,越高级的编程语言性能越差,因为意味着更多底层的封装,不过开发效率会更高,需结合使用场景做取舍)
全栈工程师修炼指南
2022/09/29
17.9K0
2.Jenkins进阶之流水线pipeline语法入门学习
kubernetes 基于jenkins spinnaker的ci/cd实践一增加制品镜像扫描
早期jenkins承担了kubernetes中的ci/cd全部功能Jenkins Pipeline演进,这里准备将cd持续集成拆分出来到spinnaker!
对你无可奈何
2021/11/20
1.1K0
Jenkins学习19 - pipline 构建项目生成 allure报告并发送邮箱
个人其实一直的不太喜欢用邮箱发送报告,测试报告用邮件通知这都是五六年前的事情了,但有部分小伙伴依然执着于发邮件报告通知。 这里整理了下发邮箱通知的教程。
上海-悠悠
2023/12/18
3180
Jenkins学习19 - pipline 构建项目生成 allure报告并发送邮箱
Jenkins集成GitLab的正确姿势,实现Git代码提交触发CI/CD
首先创建GitLab凭证,将凭证填充到 Manage Jenkins->System->enable authentication for '/project' end-point。
DevOps在路上
2024/04/22
5.2K0
Jenkins集成GitLab的正确姿势,实现Git代码提交触发CI/CD
【云原生 | Devops篇】Jenkins安装与实战
Jenkins,之前叫做Hudson,由SUN公司启动,2010年oracle收购SUN导致hudson商标归oracle保留,hudson的主要贡献者基于hudson更名为jenkins并持续更新。很长一段时间内lenkins和Hudson继续作为两个独立的项目,每个都认为对方是自己的分支。目前Hudson已经停止开发,最新的版本发布于2016年,而Jenkins的社区和开发却异常活跃。Jenkins是目前市场上使用最多的CICD工具。
Lansonli
2022/05/21
6900
【云原生 | Devops篇】Jenkins安装与实战
Jenkins input插件最佳实践(交互)
你是否用过Shell编程中的read交互式赋值? 是否用过Python中的input()函数完成在屏幕中的交互赋值操作呢?
DevOps云学堂
2019/10/31
4.8K0
基于GitLab+Jenkins的DevOps赋能实践
    随着微服务、中台架构的兴起,DevOps也变得非常关键,毕竟是一些基础设施层面的建设,如果搞好了对后面的研发工作会有很大的效率提升。关于DevOps本身的概念,网上已经非常多了,在园子里随便搜索一些都一堆概念,我就不再重复介绍了。下面直接进入正题,怎么使用GitLab+Jenkins来完成DevOps的建设。
星哥玩云
2022/07/28
9270
基于GitLab+Jenkins的DevOps赋能实践
CICD之Jenkins使用
jenkins把项目拉倒jenkins服务器,放到workspace(一般我们的源代码都在这里),开始进行流水线处理。
全栈程序员站长
2022/09/02
5300
基于Jenkins Pipeline的ASP.NET Core持续集成实践
最近在公司实践持续集成,使用到了Jenkins的Pipeline来提高团队基于ASP.NET Core API服务的集成与部署,因此这里总结一下。
Edison Zhou
2019/04/21
1.7K0
kubernetes集群使用spinnaker完成自动化部署
Spinnaker 的集群管理和部署功能两大核心功能,但是都是单独演示,没有将两者有机结合起来,今天,我们来通过一个示例来演示如何通过 Spinnaker 结合外部平台完成整个流程的自动化构建以及自动化部署到 Kubernetes 集群中。下边,我针对该示例做一些必要的说明。
王先森sec
2023/04/24
1K0
kubernetes集群使用spinnaker完成自动化部署
Jenkins流水线即代码之扩展共享库
Jenkin的多分支流水线,允许Jenkinsfile与需要 Jenkins 构建的应用程序代码放在一起,然后 Jenkins 从源代码管理系统中检出 Jenkinsfile 文件作为流水线项目构建过程的一部分并接着执行你的流水线。
追马
2020/07/15
1.9K0
Jenkins流水线即代码之扩展共享库
推荐阅读
相关推荐
「持续集成实践系列 」Jenkins 2.x 构建 CI 自动化流水线常见技巧 (二)
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档