开发者社区> aehyok> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

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

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
消息队列系列3 - 原理初探之RabbitMQ
RabbitMQ是使用Erlang语言来编写的,并且RabbitMQ是基于AMQP协议的。Erlang语言在数据交互方面性能优秀,有着和原生Socket一样的延迟,这也是RabbitMQ高性能的原因所在。可谓“人如其名”,RabbitMQ像兔子一样迅速。
28 0
2021年了,`IEnumerator`、`IEnumerable`接口还傻傻分不清楚?
IEnumerator、IEnumerable这两个接口单词相近、含义相关,傻傻分不清楚。 入行多年,一直没有系统性梳理这对李逵李鬼。
26 0
《Programming WPF》翻译 第6章 1.创建和使用资源
原文:《Programming WPF》翻译 第6章 1.创建和使用资源 资源这个词具有非常广泛的意义。任何对象都可以是一个资源。一个在用户界面中经常使用的Brush或者Color可以是一个资源。一段文本或者一个图形也可以是一个资源。
1072 0
《Programming WPF》翻译 第6章 3.二进制资源
原文:《Programming WPF》翻译 第6章 3.二进制资源 尽管ResourceDictionary和系统级别的资源适合于作为数据存在于对象中,然而,并不是所有的资源都能很好的满足这个模型。能够处理二进制流通常是很有用的。
935 0
IOS TableView学习资源
http://my.oschina.net/plumsoft/blog/51319
474 0
艾伟_转载:VS 2010 和 .NET 4.0 系列之《代码优化的Web开发Profile》篇
本系列文章导航 VS 2010 和 .NET 4.0 系列之《ASP.NET 4 中的SEO改进 》篇 VS 2010 和 .NET 4.0 系列之《干净的Web.Config文件 》篇 VS 2010 和 .
1080 0
+关注
aehyok
踏踏实实的做技术,信自己没有什么不可以。
278
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载