我们先去看看公开的.Net4.0的源程序中IEnumerable<T>、IEnumerable、IEnumerator<T>和IEnumerator这四个接口是如何声明的:

需加微信交流群的,请加小编微信号zls20210502,切记备注 加群,小编将会第一时间邀请你进群!目前一群满员,只能进二群了!

public interface IEnumerable<out T> : IEnumerable
    {
        new IEnumerator<T> GetEnumerator();
    }

public interface IEnumerator<out T> : IDisposable, IEnumerator
    {
        new T Current {
            get;
        }
    }

public interface IEnumerable
    {
        IEnumerator GetEnumerator();
    }

public interface IEnumerator
    {
        bool MoveNext();
        Object Current {
            get;
        }
    
        void Reset();
    }

一、接口IEnumerable实现

1、建一个学生数据结构和一个学生集合类:

//student数据结构
    class Student
    {
        public int id;
        public string name;
    }

//student 集合
    class StudentCollection
    {
        public List<Student> students = new List<Student>();
        public void Add(Student student)
        {
            students.Add(student);
        }
    }

公开一个Add()方法以添加数据,我们的集合类建立完毕。下来添加数据:

static void Main(string[] args)
        {
            StudentCollection sc = new StudentCollection();
            sc.Add(new Student { id=0,name="Tony"});
            sc.Add(new Student { id=1,name="Micheal"});
            sc.Add(new Student { id =2, name = "Amy" });
            foreach(var s in sc) {...}
        }
    }

当我们想用foreach()遍历的时候,编译器会告诉我们StudentCollection不包含GetEnumerator,不能用foreach遍历。虽然StudentCollection里面有能用遍历的List<T>,但我们不想在属性上迭代,我们想在类上迭代,不能 foreach(var s in sc.students){...}

现在只有把我们的StudentCollection类改造成能foreach的。

2、继承接口IEnumerable:

当我们在类后面加上:IEnumerable后,Visual Studio IDE会冒出来一个小黄灯泡,点进去有提示自动填充接口的约定,我们选第一项实现接口(Visaul Studio是全世界最贴心的IDE!),IDE会帮我们把SudentCollection改造成以下的:

class StudentCollection:IEnumerable
    {
        public List<Student> students = new List<Student>();
        public void Add(Student student)
        {
            students.Add(student);
        }

public IEnumerator GetEnumerator()
        {
            throw new NotImplementedException();
        }
    }

加了一个返回迭代器的方法GetEnumrator。下来按照IEnumetator接口的约定来实现我们的迭代器StudentCollectionEnumerator,用IDE自动补全代码如下:

//迭代器
    class StudentCollectionEnumerator : IEnumerator
    {
        public object Current
        {
            get
            {
                throw new NotImplementedException();
            }
        }

public bool MoveNext()
        {
            throw new NotImplementedException();
        }

public void Reset()
        {
            throw new NotImplementedException();
        }
    }

我的理解是:Current返回当前元素,MoveNext移动到下一个,Reset回到第一个元素。但根据MSDN上面的说法,Reset 方法提供的 COM 互操作性。它不一定需要实现;相反,实施者只需抛出NotSupportedException。但是,如果您选择执行此操作,则应确保没有调用方依赖于Reset功能。

迭代器工作的原理是:先调用MoveNext()方法,然后读取Current得到元素,直到MoveNext返回false。

我们需要3个字段分别放置 元素的位置、元素、元素集。改变后的程序如下:

//迭代器
    class StudentCollectionEnumerator : IEnumerator
    {
        private int _index;
        private List<Student> _collection;
        private Student value;
        public StudentCollectionEnumerator(List<Student> colletion)
        {
            _collection = colletion;
            _index = -1;
        }
         object IEnumerator.Current
        {
            get { return value; }
        }
        public bool MoveNext()
        {
            _index++;
            if (_index >= _collection.Count) { return false; }
            else { value = _collection[_index]; }
            return true;
        }
        public void Reset()
        {
            _index = -1;
        }

}

首先,迭代器初始化,引入元素集 _collection,并把索引 _index设置成-1。设置成-1而不是0是因为迭代器首先调用MoveNext,在MoveNext里面我们先把索引+1指向下一个元素,如果索引_index的值初始为0,则第一个元素是元素集[1],第二个元素了。
其次,我们要把object Current改成 IEnumerator.Current,这个是实现迭代器的关键。返回元素。(好像有装箱的行为)
第三,在MoveNext方法内累加索引,并从元素集中读取元素。然后让索引值超出元素集返回个false值。
最后,在Reset方法内让索引值为-1,不过好像直接抛出错误也成。

迭代器写好了,我们在StudentColletion类里面调用:

class StudentCollection : IEnumerable
    {
        public List students;
        public StudentCollection()
        {
            students = new List();
        }
        public void Add(Student student)
        {
            students.Add(student);
        }
        public IEnumerator GetEnumerator()
        {
            return new StudentCollectionEnumerator(students);
        }
    }

测试运行一下,大功告成!我们实现了可枚举的自己的类。

通过观察,发现迭代器主要就是返回一个元素对象,而StudentColletion里面的students元素集是List的,本身就能枚举,我们能不能利用这个不用专门写迭代器来实现枚举呢?
答案是肯定的,我们这样写:

class StudentCollection:IEnumerable
    {
        public List<Student> students = new List<Student>();
        public void Add(Student student)
        {
            students.Add(student);
        }

public IEnumerator GetEnumerator()
        {
            foreach(var s in students)
            {
                yield return s;
            }
        }
    }

这样就能实现枚举了,真简单,充分利用了.Net给出的各种可枚举集合,不用再去写GetEnumerator这种累活了。

二、接口IEnumerable<T>实现

如果我们想写一个通用的可foreach的类,用到泛型:

class MyCollection<T>
    {
        public List<T> mycollection = new List<T>();
        public void Add(T value)
        {
            mycollection.Add(value);
        }
    }

其实这个MyCollection类只不过是在List<T>外面封装了一层,要实现IEnumable<T>,继承该泛型接口,Visual Studio 的IDE自动帮我们补全后,如下:

class MyCollection:IEnumerable
    {
        public List mycollection = new List();
        public void Add(T value)
        {
            mycollection.Add(value);
        }
        public IEnumerator GetEnumerator()
        {
            throw new NotImplementedException();
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            throw new NotImplementedException();
        }
    }

我们直接用上面第二个简单的写法,改成:

class MyCollection:IEnumerable
    {
        public List mycollection = new List();
        public void Add(T value)
        {
            mycollection.Add(value);
        }
        public IEnumerator GetEnumerator()
        {
            foreach(var s in mycollection)
            {
                yield return s;
            }
        }
        IEnumerator IEnumerable.GetEnumerator()
        {
            foreach (var s in mycollection)
            {
                yield return s;
            }
        }
    }

测试运行:

static void Main(string[] args)
        {
            MyCollection mc = new MyCollection();
            mc.Add(0);
            mc.Add(1);
            mc.Add(2);
            foreach(var s in mc) { Console.WriteLine(s); }
            Console.ReadKey();
    }

大功告成!
虽然第二种写法比较投机,充分利用了.NET Framework给的各种泛型集合可枚举的特征。不过我们也自己实现了一个GetEnumerator(),了解了枚举器的工作原理。本章学习目的达成。

C# 常用接口学习 IEnumerableT相关推荐

  1. 7 种 Javascript 常用设计模式学习笔记

    7 种 Javascript 常用设计模式学习笔记 由于 JS 或者前端的场景限制,并不是 23 种设计模式都常用. 有的是没有使用场景,有的模式使用场景非常少,所以只是列举 7 个常见的模式 本文的 ...

  2. 盘点springmvc的常用接口

    2019独角兽企业重金招聘Python工程师标准>>> 盘点springmvc的常用接口### springmvc是如今非常流行的web开发框架之一.我个人非常喜欢它约定优于配置的理 ...

  3. 【LeetCode 总结】Leetcode 题型分类总结、索引与常用接口函数

    文章目录 零. Java 常用接口函数 一. 动态规划 二. 链表 三. 哈希表 四. 滑动窗口 五. 字符串 六. DFS.BFS 七. 二分法 八. 二叉树 九. 偏数学.过目不忘 and 原地算 ...

  4. 6-3-1:STL之vector——vector的快速入门、常用接口

    文章目录 一:vector介绍 二:vector的常用接口 (1)构造 (2)迭代器 (3)容量操作 (4)元素访问 (5)增删查改 一:vector介绍 vector是一个可变大小数组的序列容器.和 ...

  5. 6-2-1:STL之string——string的快速入门、常用接口

    文章目录 一:几个问题 二:学习string的常用接口 (1)string类对象的构造 (2)容量操作 (3)访问操作 (4)迭代器 (5)修改操作 (6)非成员函数 一:几个问题 1:为什么学习st ...

  6. 常用增强学习实验环境 II (ViZDoom, Roboschool, TensorFlow Agents, ELF, Coach等)

    原文链接:http://blog.csdn.net/jinzhuojun/article/details/78508203 前段时间Nature上发表的升级版Alpha Go - AlphaGo Ze ...

  7. 【学习总结】-Apsara Clouder专项技能认证:实现调用API接口学习总结

    Apsara Clouder专项技能认证:实现调用API接口-学习总结 API的概念: API的特点: API的分类: 为什么要使用API 阿里云API市场 API请求与认证 Web API协议 HT ...

  8. 常用增强学习实验环境 I (MuJoCo, OpenAI Gym, rllab, DeepMind Lab, TORCS, PySC2)

    常用增强学习实验环境 I (MuJoCo, OpenAI Gym, rllab, DeepMind Lab, TORCS, PySC2) 标签: 强化学习OpenAI GymMuJoCoStarCra ...

  9. 阿里云Apsara Clouder专项技能认证-实现调用API接口-学习笔记

    Apsara Clouder专项技能认证-实现调用API接口-学习笔记 阿里云的一个小认证,闲来无事,考一下 一.API简介 API的概念 API(Application Programming In ...

最新文章

  1. 关于Flutter初始化流程,我必须告诉你的是...
  2. Leetcode 162. 寻找峰值 解题思路及C++实现
  3. Redis Sentinel配置小记
  4. 从 0 开始手写一个 Spring MVC 框架,向高手进阶
  5. 我的世界光影mod怎么用_玩转光影!闪光灯、反光板怎么用才高级?
  6. ECCV18 | 如何正确使用样本扩充改进目标检测性能(附Github地址)
  7. 判断回文串时忽略既非字母又非数字的字符
  8. @程序员,让8年京东架构师为你解析云原生监控和日志解决方案!
  9. 听说你决定当全职自由漏洞猎人了?过来人想跟你聊聊
  10. 【网络/通信】概念的理解 —— 带宽、吞吐量、净荷
  11. redis中集群的故障恢复
  12. Qt QDialog简介
  13. EBS R12.2 ADOP (R12.2 AD Online Patching) - 3
  14. 小程序中实现搜索功能
  15. 浅谈大数据时代之影响力
  16. axios请求下载excel文件以及文件乱码问题
  17. 【新手入门必看】git 和 github 介绍
  18. 性能优化系列(五)网络性能优化
  19. 语音对讲广播转发模块
  20. TokenGazer:DeFi领域发展良好,量化模型显示MKR处于市值偏低区间

热门文章

  1. Hsiaoyang:Google搜索结果页面分析
  2. html5波浪线条,HTML5 svg炫酷波浪线条动画插件
  3. php发送get、post请求的几种方法
  4. asp.net core结合NLog搭建ELK实时日志分析平台
  5. Error opening terminal: xterm-256color
  6. 【转】博客美化(1)基本后台设置与样式设置
  7. stm32 usmart使用
  8. Bootstrap入门(八)组件2:下拉菜单
  9. Oracle免客户端InstantClient安装使用
  10. VHDL 整数 小数 分数 分频