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

原文:C#基础知识系列九(对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关键字的用法。  

时间: 2024-09-28 12:48:57

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

PHP新手入门篇基础知识(九)

一个简单交互的网站实例-简易banner动态更替(六) 9. 简易banner动态更替 不知大家有没有发现各大站点上的标头广告banner,我们每次访问这些站点时,都会看到不同的广告图标,或者如果你每次刷新页面时,这些广告banner就会不断地随机更替变换.要实现这种效果虽然用javascript也可以达到(象天极网站的动态变换广告banner就是通过调用javascript来实现的),但是如果我们用PHP的话,我们还可以结合数据库来做数据量很大,如每日一题之类的功能.费话少说,让我们立即来看看

Linux操作系统基础知识之九:设备驱动

Q1.        为什么把设备分为"块设备"和"字符设备"两大类? A: 1)        Linux将设备看成文件,具有三方面的含义:第一,每个设备都对应一个文件名,在内核中也就对应一个索引节点:第二,对文件操作的系统调用大都适用于设备文件:第三,从应用程序的角度看,设备文件的逻辑空间是一个线性空间:对于同一个具体的设备而言,文件操作和设备驱动是同一个事物的不同层次,概念上可以将一个系统划分为应用.文件系统和设备驱动三个层次: 2)        Linux

推荐给新手学习seo基础知识的十本SEO书籍

从2011年起,网络上盛起多家seo优化培训机构,纷纷大包大揽的设堂招生.培训的课程多为一个月时间,学生学习的是被称为"实战"的口语化教程.主要是教大家如何去操作网站SEO优化,如怎么写网站的标签,网站如何布局,在哪里做外链等等.教出来的学生很多都是"只会做,不会想",遇到一些与seo优化培训中不一样的状况就不知道怎么去处理了. seo是一门学问,它是针对于搜索引擎的一门学问.既然是一门学问,想弄懂它就必须从基础开始学习,学习最简单的seo基础知识,慢慢的去深入,最

C#中如何构建可枚举类型(IEnumerable和IEnumerator)

为了开始对实现既有接口的了解,我们就看一下IEnumerable和IEnumerator的作用,想一下,C#支持关键字foreach,允许我们遍历任何数组类型的内容: //遍历数组的项 int[] myArray={10,20,30} foreach(int i in myArray) {......} 虽然看上去只有数组类型才能使用这个结构,其实任何支持GetEnumerator()方法的类型都可以通过foreach结构进行运算,举例说明,我们新建一个项目. 首先,我们创建一个类 public

IEnumerable和IEnumerator 详解 (转)

原文链接:http://blog.csdn.net/byondocean/article/details/6871881 参考链接:http://www.cnblogs.com/hsapphire/archive/2010/04/16/1713211.html   初学C#的时候,老是被IEnumerable.IEnumerator.ICollection等这样的接口弄的糊里糊涂,我觉得有必要切底的弄清楚IEnumerable和IEnumerator的本质. 下面我们先看IEnumerable和

【Xamarin开发 Android 系列 4】 Android 基础知识

原文:[Xamarin开发 Android 系列 4] Android 基础知识 什么是Android?   Android一词的本义指"机器人",同时也是Google于2007年11月5日宣布的基于Linux平台的开源手机操作系统的名称,该平台由操作系统.中间件.用户界面和应用软件组成,而且不存在任何以往阻碍移动产业创新的专有权障碍,号称是首个为移动终端打造的真正开放和完整的移动软件. Android是一种以Linux为基础的开放源代码操作系统,主要使用于便携设备.目前尚未有统一中文

iOS开发系列--C语言之基础知识

当前移动开发的趋势已经势不可挡,这个系列希望浅谈一下个人对IOS开发的一些见解,这个IOS系列计划从几个角度去说IOS开发: C语言 OC基础 IOS开发(iphone/ipad) Swift 这么看下去还有大量的内容需要持续补充,但是今天我们从最基础的C语言开始,C语言部分我将分成几个章节去说,今天我们简单看一下C的一些基础知识,更高级的内容我将放到后面的文章中. 今天基础知识分为以下几点内容(注意:循环.条件语句在此不再赘述): Hello World 运行过程 数据类型 运算符 常用函数

[python] 专题九.Mysql数据库编程基础知识

        在Python网络爬虫中,通常是通过TXT纯文本方式存储,其实也是可以存储在数据库中的:同时在WAMP(Windows.Apache.MySQL.PHP或Python)开发网站中,也可以通过Python构建网页的,所以这篇文章主要讲述Python调用MySQL数据库相关编程知识.从以下几个方面进行讲解:         1.配置MySLQ         2.SQL语句基础知识         3.Python操作MySQL基础知识         4.Python调用MySQL

Java核心技术 卷Ⅰ 基础知识(原书第10版)

Java核心技术系列 Java核心技术 卷Ⅰ 基础知识 (原书第10版) Core Java Volume I-Fundamentals (10th Edition) [美] 凯S.霍斯特曼(Cay S. Horstmann) 著 周立新 陈 波 叶乃文 邝劲筠 杜永萍 译 图书在版编目(CIP)数据 Java核心技术 卷Ⅰ 基础知识(原书第10版) / (美)凯S. 霍斯特曼(Cay S. Horstmann)著:周立新等译. -北京:机械工业出版社,2016.8 (Java核心技术系列) 书