2021年了,`IEnumerator`、`IEnumerable`接口还傻傻分不清楚?

简介: IEnumerator、IEnumerable这两个接口单词相近、含义相关,傻傻分不清楚。入行多年,一直没有系统性梳理这对李逵李鬼。

IEnumerator


IEnumerator、IEnumerable接口有相似的名称,这两个接口通常也在一起使用,它们有不同的用途。


IEnumerator接口为类内部的集合提供了迭代方式, IEnumerator 要求你实现三个方法:


  • MoveNext方法:该方法将集合索引加1,并返回一个bool值,指示是否已到达集合的末尾。


  • Reset方法:它将集合索引重置为其初始值-1,这会使枚举数无效。


  • Current方法: 返回position位置的当前对象


IEnumerable


IEnumerable接口为foreach迭代提供了支持,IEnumerable要求你实现GetEnumerator方法。


public IEnumerator GetEnumerator()
{
    return (IEnumerator)this;
}


该用哪一个接口?


仅凭以上辞藻,很难区分两个接口的使用场景。


IEnumerator接口定义对类中的集合类型对象的迭代方式


IEnumerable接口允许使用foreach循环进行枚举。


因此IEnumerable接口的GetEnumerator方法会返回一个IEnumerator接口。要实现IEnumerable,你还必须实现IEnumerator


从英文词根上讲:

IEnumerator接口代表了枚举器,里面定义了枚举方式,是名词。

IEnumerable接口代表该对象具备了可被枚举的性质,是形容词。


总之,如果您想提供对foreach的支持,那么就先让对象可枚举,再谈论枚举方式,也就是说实现这两个接口。


最佳实践


  • 在嵌套类中实现IEnumerator,这样你可以创建多个枚举器。


  • 为IEnumerator的Current方法提供异常处理。


为什么要这么做?


如果集合的内容发生变化,则
reset方法将被调用,紧接着当前枚举数无效,您将收到一个IndexOutOfRangeException异常(其他情况也可能导致此异常)。所以执行一个Try…Catch块来捕获这个异常并引发InvalidOperationException异常, 提示在迭代时不允许修改集合内容


这也正是我们常见的在foreach 里面尝试修改迭代对象会报InvalidOperationException异常的原因。


下面以汽车列表为例实现IEnumerator IEnumerable接口


using System;
using System.Collections;
namespace ConsoleEnum
{
    public class cars : IEnumerable
    {
        private car[] carlist;
        //Create internal array in constructor.
        public cars()
        {
            carlist= new car[6]
            {
                new car("Ford",1992),
                new car("Fiat",1988),
                new car("Buick",1932),
                new car("Ford",1932),
                new car("Dodge",1999),
                new car("Honda",1977)
            };
        }
        //private enumerator class
        private class  MyEnumerator:IEnumerator
        {
            public car[] carlist;
            int position = -1;
            //constructor
            public MyEnumerator(car[] list)
            {
                carlist=list;
            }
            private IEnumerator getEnumerator()
            {
                return (IEnumerator)this;
            }
            //IEnumerator
            public bool MoveNext()
            {
                position++;
                return (position < carlist.Length);
            }
            //IEnumerator
            public void Reset()
            {
                position = -1;
            }
            //IEnumerator
            public object Current
            {
                get
                {
                    try
                    {
                        return carlist[position];
                    }
                    catch (IndexOutOfRangeException)
                    {
                        throw new InvalidOperationException();
                    }
                }
            }
        }  //end nested class
      public IEnumerator GetEnumerator()
      {
          return new MyEnumerator(carlist);
      }
    }
}


foreach cars的时候,可以明显看到


  • foreach语法糖初次接触可枚举的cars, 实际会访问cars实现的 GetEnumerator()方法,拿到迭代器


  • foreach每次迭代,实际会访问迭代器的Current属性
相关文章
|
5月前
|
Java
揭秘Java多态:为何同一消息,对象们却各有“心思”?
【6月更文挑战第17天】Java中的多态性让不同对象对同一方法有独特响应。以动物园为例,抽象类`Animal`定义`makeSound()`,子类如`Tiger`, `Lion`, `Monkey`继承并重写该方法。通过`Animal`引用调用,实际执行子类实现,展示动态绑定的威力。多态提升代码灵活性,支持扩展而无需改动原有代码,体现面向对象的核心思想。
25 2
|
5月前
|
Java
Java多态:如何实现“一箭双雕”的编程艺术?
【6月更文挑战第17天】Java中的多态是编程灵活性的关键,它允许通用接口处理不同类型的对象。通过抽象基类或接口,子类可以实现各自的行为。例如,在动物音乐会场景中,一个`Animal`接口让狮子、猴子和企鹅都能唱歌,调用`sing()`即自动匹配相应行为。同样,在图形绘制示例中,`Shape`基类让绘制圆形、正方形和三角形变得简单,只需调用`draw()`。多态减少了代码冗余,增强了可扩展性和可维护性,是解决需求变化的利器。
36 0
|
存储 安全 编译器
【巧妙继承】C++玩转继承的神级技巧
【巧妙继承】C++玩转继承的神级技巧
关于Java继承,咱不得不说的4个要点
关于Java继承,咱不得不说的4个要点
105 0
|
Serverless C语言 Python
学编程这么久,还傻傻分不清什么是方法(method),什么是函数(function)?
在标准库inspect 中,它提供了两个自省的函数,即 ismethod() 和 isfunction(),可以用来判断什么是方法,什么是函数。
313 0
学编程这么久,还傻傻分不清什么是方法(method),什么是函数(function)?
|
Java 编译器 C++
不懂泛型,怎么装逼,一文把泛型说的明明白白,安排!!!
泛型是Java中的高级概念,也是构建框架必备技能,比如各种集合类都是泛型实现的,今天详细聊聊Java中的泛型概念,希望有所收获。记得点赞,关注,分享哦。
130 0
不懂泛型,怎么装逼,一文把泛型说的明明白白,安排!!!
HashMap中傻傻分不清楚的那些概念
很多人在通过阅读源码的方式学习Java,这是个很好的方式。而JDK的源码自然是首选。在JDK的众多类中,我觉得HashMap及其相关的类是设计的比较好的。很多人读过HashMap的代码,不知道你们有没有和我一样,觉得HashMap中关于容量相关的参数定义的太多了,傻傻分不清楚。
HashMap中傻傻分不清楚的那些概念
|
Java 数据库
后浪拍前浪-覆写父类方法 | 带你学《Java面向对象编程》之三十九
既然出现了继承的关系,那么就存在子类和父类的联系,而在子类之中有可能定义和父类完全相同的方法或属性的名称,这个时候就称为覆写。
后浪拍前浪-覆写父类方法   | 带你学《Java面向对象编程》之三十九
|
Java 数据库 数据安全/隐私保护
规则之间-方法覆写限制 | 带你学《Java面向对象编程》之四十
本节向读者介绍了覆写方法过程中的一些限制,并拓宽读者对访问权限控制符的认识,帮助读者进行有效的方法覆写。
|
Java 数据库
再生与终结-初识属性覆盖与final | 带你学《Java面向对象编程》之四十一
本节将为读者介绍属性覆盖和final关键字相关内容,并为读者展示如何在Java中定义一个“常量”。