C# 常用接口学习 IEnumerable<T>
作者:乌龙哈里
时间: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>的更多相关文章
- C# 常用接口学习 ICollection<T>
C# 常用接口学习 ICollection<T> 作者:乌龙哈里 时间:2015-11-01 平台:Window7 64bit,Visual Studio Community 2015 参 ...
- C# 常用接口学习 IComparable 和 IComparer
C# 常用接口学习 IComparable 和 IComparer 作者:乌龙哈里 时间:2015-11-01 平台:Window7 64bit,Visual Studio Community 201 ...
- JDBC 学习笔记(三)—— JDBC 常用接口和类,JDBC 编程步骤
1. JDBC 常用接口和类 DriverManager 负责管理 JDBC 驱动的服务类,程序中主要的功能是获取连接数据库的 Connection 对象. Connection 代表一个数据库连接对 ...
- 常用接口简析3---IList和List的解析
常用接口的解析(链接) 1.IEnumerable深入解析 2.IEnumerable.IEnumerator接口解析 3.IComparable.IComparable接口解析 学习第一步,先上菜: ...
- Java实战之02Hibernate-01简介、常用接口、CRUD操作
一.Hibernate简介 1.Hibernate在开发中所处的位置 2.ORM映射 Object :面向对象领域的 Relational:关系数据库领域的 Mapping:映射 Object: Re ...
- python之路,Day24 常用设计模式学习
python之路,Day24 常用设计模式学习 本节内容 设计模式介绍 设计模式分类 设计模式6大原则 1.设计模式介绍 设计模式(Design Patterns) --可复用面向对象软件的基础 ...
- Day5 - Python基础5 常用模块学习
Python 之路 Day5 - 常用模块学习 本节大纲: 模块介绍 time &datetime模块 random os sys shutil json & picle shel ...
- 【转】Android LCD(二):LCD常用接口原理篇
关键词:android LCD TFT TTL(RGB) LVDS EDP MIPI TTL-LVDS TTL-EDP 平台信息:内核:linux2.6/linux3.0系统:android/ ...
- 常用接口简析1---IEnumerable、IEnumerator简析
常用接口的解析(链接) 1.IEnumerable深入解析 2.IComparable.IComparable接口解析 3.IList.IList接口解析 引言: IEnumerable和I ...
随机推荐
- 【详细贴】Ubuntu Linode搭建海外策略路由VPN IPSec+L2TP(一)
对于码农来说,Google上搜索,stackoverflow上提问,twitter上交流,这样的国际化开放平台才是码农成长的空间.但是,目前遇到的一些现实的困难,无可避免的带来一些麻烦,不过这难不倒我 ...
- Solr之NamedList 简单介绍与实例解析
大家都知道,Solr是一个基于Lucene高可配置的搜索服务器,大部分参数值以及相关优化等等都可以在solrconfig.xml中配置,那么就需要一个能够很快的进行解析和读取配置文件内容的数据结构,为 ...
- zoom:1的作用
兼容IE6.IE7.IE8浏览器,经常会遇到一些问题,可以使用zoom:1来解决,有如下作用:触发IE浏览器的haslayout解决ie下的浮动,margin重叠等一些问题. 如下使用DIV做一行两列 ...
- MVC源码解析 - Http Pipeline 解析(下)
接上一篇, 我在 HttpModule 的Init方法中, 添加了自己的事件, 在Pipeline里, 就会把握注册的事件给执行了. 那么Pipeline是如何执行并且按照什么顺序执行的呢? 现在我们 ...
- C++继承、多态、虚函数
#答案:http://www.cnblogs.com/tinaluo/articles/6290994.html 1 编写程序:定义一个抽象基类sharpe,只有包含一个求面积的纯虚函数,派生出矩形类 ...
- 关于【IE兼容】的都在这
滚动条 ie的滚动条覆盖了内容,为了触屏操作优化浏览器的内容显示,IE 浏览器提供了一种可以浮动显示,自动隐藏的滚动条样式,但是这个样式会在某些情况下造成一些困扰,比如下图... 其实默认情况下,桌面 ...
- 论JSON的重要性☞异步上传过程中data取多组值的方法
异步上传取多组值一起post改变属性 如图: 看图说话
- createElement、createTextNode、setAttribute使用方法
createElement() 方法可创建元素节点. 示例:var placeholder = document.createElement("img"); createTextN ...
- NYOJ-括号配对问题 <技巧性的非栈道法>
括号配对问题 时间限制:3000 ms | 内存限制:65535 KB 难度:3 描述 现在,有一行括号序列,请你检查这行括号是否配对. 输入 第一行输入一个数N(0<N<=100) ...
- mysql分享记录
今天公司进行了一次mysql的分享,做一下记录,也许很多东西很简单,但是对于不知道的人就很难 1.用最小代价存储 举个例子来说,如果能确定某个字段是存数字的,并且数字的大小一定不会超过127或255, ...