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


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

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

    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,你就不能将IEnumerableGetEnumerator方法的返回值转换为IEnumerator接口。

    从英文词根上讲:
    IEnumerator接口代表了枚举器,里面定义了枚举方式;
    IEnumerable接口代表该对象具备了可被枚举的性质。

    总之,IEnumerable的使用要求类实现IEnumerator。如果您想提供对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属性的get访问器
  • 相关阅读:
    windows下安装php5.5的redis扩展
    redis常见命令
    HDU 5869 Different GCD Subarray Query
    WA时查错点
    HDU 3333 Turing Tree
    HDU 5868 Different Circle Permutation
    AcWing 272 最长公共上升子序列 (dp)
    中国计量大学现代科技学院第四届“中竞杯”程序设计校赛 I 题 (双端队列bfs / 优先队列bfs)
    AtCoder ARC 109 D (拆点 + 分类讨论)
    codeforces 1408D. Searchlights (暴力 + 前缀优化)
  • 原文地址:https://www.cnblogs.com/JulianHuang/p/14271285.html
Copyright © 2020-2023  润新知