首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >2021年了,`IEnumerator`、`IEnumerable`接口还傻傻分不清楚?

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

作者头像
有态度的马甲
发布2021-01-21 11:24:40
发布2021-01-21 11:24:40
3.5K1
举报
文章被收录于专栏:精益码农精益码农
IEnumeratorIEnumerable这两个接口单词相近、含义相关,傻傻分不清楚。 入行多年,一直没有系统性梳理这对李逵李鬼。

最近本人在怼着why神的《其实吧,LRU也就那么回事》,方案1使用数组实现LRU,手写算法涉及这一对接口,借此时机覆盖这一对难缠的冤家。

IEnumerator

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

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

  • MoveNext方法:该方法将集合索引加1,并返回一个bool值,指示是否已到达集合的末尾。
  • Reset方法:它将集合索引重置为其初始值-1,这会使枚举数无效。
  • Current方法: 返回position位置的当前对象

IEnumerable

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

代码语言:javascript
复制
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接口

代码语言:javascript
复制
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属性
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-01-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 精益码农 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • IEnumerator
  • IEnumerable
  • 该用哪一个接口?
  • 最佳实践
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档