作者:乌龙哈里
时间:2015-10-24
平台:Window7 64bit,Visual Studio Community 2015

本文参考:

本文章节:

  • 接口IEnumerable实现
  • 接口IEnumerable<T>实现

正文:

本文是作者摸索学习.Net的过程,逐步进行,比较繁琐,是作者本人用来帮助记忆的博文。

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

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# 常用接口学习 IEnumerable<T>的更多相关文章

  1. C# 常用接口学习 ICollection<T>

    C# 常用接口学习 ICollection<T> 作者:乌龙哈里 时间:2015-11-01 平台:Window7 64bit,Visual Studio Community 2015 参 ...

  2. C# 常用接口学习 IComparable 和 IComparer

    C# 常用接口学习 IComparable 和 IComparer 作者:乌龙哈里 时间:2015-11-01 平台:Window7 64bit,Visual Studio Community 201 ...

  3. JDBC 学习笔记(三)—— JDBC 常用接口和类,JDBC 编程步骤

    1. JDBC 常用接口和类 DriverManager 负责管理 JDBC 驱动的服务类,程序中主要的功能是获取连接数据库的 Connection 对象. Connection 代表一个数据库连接对 ...

  4. 常用接口简析3---IList和List的解析

    常用接口的解析(链接) 1.IEnumerable深入解析 2.IEnumerable.IEnumerator接口解析 3.IComparable.IComparable接口解析 学习第一步,先上菜: ...

  5. Java实战之02Hibernate-01简介、常用接口、CRUD操作

    一.Hibernate简介 1.Hibernate在开发中所处的位置 2.ORM映射 Object :面向对象领域的 Relational:关系数据库领域的 Mapping:映射 Object: Re ...

  6. python之路,Day24 常用设计模式学习

    python之路,Day24 常用设计模式学习   本节内容 设计模式介绍 设计模式分类 设计模式6大原则 1.设计模式介绍 设计模式(Design Patterns) --可复用面向对象软件的基础 ...

  7. Day5 - Python基础5 常用模块学习

    Python 之路 Day5 - 常用模块学习   本节大纲: 模块介绍 time &datetime模块 random os sys shutil json & picle shel ...

  8. 【转】Android LCD(二):LCD常用接口原理篇

    关键词:android LCD TFT TTL(RGB)  LVDS  EDP MIPI  TTL-LVDS  TTL-EDP 平台信息:内核:linux2.6/linux3.0系统:android/ ...

  9. 常用接口简析1---IEnumerable、IEnumerator简析

    常用接口的解析(链接) 1.IEnumerable深入解析 2.IComparable.IComparable接口解析 3.IList.IList接口解析      引言: IEnumerable和I ...

随机推荐

  1. 超级强悍的PHP代码编辑器PHPstorm及配置

    如何下载安装 官方网站:http://www.jetbrains.com/phpstorm/,本篇文章展示的是5.0版本以,所以,如果你的软件版本过高,可能可有误,所以,如果有问题,请在本站留言,做为 ...

  2. android 工程里缺少 R.java 文件原因和解决方法

    作为新手,学习android 的时候难免要导入一些示例,目的为了更加了解android各种API用法,顺便也可以学习下别人代码的写法. 可是导入android源码后,基本都有错误,R.java也不会自 ...

  3. Kendo UI开发教程(9): Kendo UI Validator 概述

    Kendo UI Validator 支持了客户端校验的便捷方法,它基于HTML 5 的表单校验功能,支持很多内置的校验规则,同时也提供了自定义规则的便捷方法. 完整的Kendo UI 的Valida ...

  4. cocos2d-x场景切换与过渡效果

    场景切换 void  MyScene::daySceneCallback(CCObject *pSender) {     CCScene *scene =  new  MyScene();      ...

  5. Physical Standby Database Failover

    1.物理standby failover 切换 故障转移时在一些糟糕的事情发生时执行的计划外事件,需要将生产库移动到DR站点.有意思的是,这时候人们通常忙来忙去,试图弄明白发生了什么,需要做些什么才能 ...

  6. php memcached+Mysql(主从)

    /* index.php   程序入口,用来构造sql(如查询,更新) config.php  配置参数(memcache,mysql) init.php    封装memcached操作(memca ...

  7. C++malloc,calloc,realloc,free函数

    在进行C/c++编程的时候,需要程序员对内存的了解比较清楚,经常需要操作的内存可分为下面几个类别:     1.堆栈区(stack):由编译器自动分配与释放,存放函数的参数值,局部变量,临时变量等等, ...

  8. 项目中出现黄色感叹号 教你去掉Myeclipse的黄色感叹号

    点击Window>>Preferences>>General>>Editors>>Text Editors>>Annotations 选中右 ...

  9. 第一百三十一节,JavaScript,封装库--CSS

    JavaScript,封装库--CSS 将封装库里的方法,改成了原型添加方法 增加4个方法 tian_jia_class()方法,给获取到的元素添加class属性,参数是class属性值,可以连缀1 ...

  10. mysqldump 备份直接至压缩文件,提高备份及压缩时间

    1.备份及压缩分开 备份mysqldump --single-transaction -hlocalhost --all-databases --triggers --routines --event ...