C# IEnumerable 和 IEnumerator接口浅析
温故而知新,可以为师矣,有空经常复习一下基础知识是有必要的,并且能加深理解和记忆。
Foreach常用于循环访问集合,对实现IEnumerable的接口的容器进行遍历,IEnumerable和IEnumerator接口我有时候也有点迷糊,按官方的解释,IEnumerable是枚举器接口,IEnumerator是迭代器接口,从字面意思来看相差不大,逐一分析一下。
IEnumerable接口
public interface IEnumerable
{
IEnumerator GetEnumerator();
}
继承IEnumerable接口的类需实现暴露出来的GetEnumerator()方法,并返回一个IEnumerator接口对象,看来真正做事的是IEnumerator,F12看一下IEnumerator又有什么鬼东西。
IEnumerator接口
public interface IEnumerator
{
object Current { get; }
bool MoveNext();
void Reset();
}
IEnumerator接口有三个东东,一个属性Current,返回当前集合中的元素,方法MoveNext()移动到下一个,遍历不都是向后遍历的嘛,Reset(),字面意思重置,这个容易理解。做个假设:既然IEnumerable接口返回是IEnumerator接口迭代器来实现的,那么仅继承IEnumerator迭代器接口能不能实现一个自定义容器?
定义一个Phone类
public class Phone
{
public string Name;
public Phone(string name)
{
this.Name = name;
}
}
定义一个名为MyEnumerator迭代器,并现实它接口IEnumerator
public class MyEnumerator : IEnumerator
{
Phone[] p;
int idx = -;
public MyEnumerator(Phone[] t)
{
p = t;
}
public object Current
{
get
{
if (idx == -)
return new IndexOutOfRangeException();
return p[idx];
}
} public bool MoveNext()
{
idx++;
return p.Length > idx;
} public void Reset()
{
idx = -;
}
}
class Program
{
static void Main(string[] args)
{
show("-----------IEnumerator------------");
Phone[] phones = new Phone[] { new Phone("iPhone 7s"), new Phone("iPhone 6s"), new Phone("iPhone 5s") };
MyEnumerator enumerator = new MyEnumerator(phones);
while (enumerator.MoveNext())
{
Phone p = enumerator.Current as Phone;
show(p.Name);
}
Console.ReadKey();
}
static void show(string i)
{
Console.WriteLine(i);
}
}
结果显示:
果然不出所料,真正做事情的是IEnumerator接口,即可循环访问自定义的一个容器,不过,初衷是想用Foreach来做循环访问、遍历的。那好,那就只能显示IEnumerable接口来做。稍稍改造一下Phone类:
public class Phone : IEnumerable
{
public string Name ;
public Phone(string name)
{
this.Name = name;
} Phone[] p;
public Phone(Phone[] t)
{
p = t;
}
public IEnumerator GetEnumerator()
{
return new MyEnumerator(p);
}
}
static void Main(string[] args)
{
show("-----------IEnumerator------------");
Phone[] phones = new Phone[] { new Phone("iPhone 7s"), new Phone("iPhone 6s"), new Phone("iPhone 5s") };
MyEnumerator enumerator = new MyEnumerator(phones);
while (enumerator.MoveNext())
{
Phone p = enumerator.Current as Phone;
show(p.Name);
} show("-----------IEnumerable------------");
Phone phoneList = new Phone(phones);
foreach (Phone p in phoneList)
{
show(p.Name);
} Console.ReadKey();
}
结果显示:
大功告成,再扩展成通用的容器PhonePackage,继承泛型IEnumerable<T>接口即可。
public class PhonePackage<T> : IEnumerable<T>
{
private List<T> dataList = null; public void Add(T t)
{
if (dataList == null)
dataList = new List<T>();
dataList.Add(t);
} public IEnumerator<T> GetEnumerator()
{
foreach (T t in dataList)
{
yield return t;
}
} IEnumerator IEnumerable.GetEnumerator()
{
foreach (T t in dataList)
{
yield return t;
}
}
}
static void Main(string[] args)
{
show("-----------IEnumerator------------");
Phone[] phones = new Phone[] { new Phone("iPhone 7s"), new Phone("iPhone 6s"), new Phone("iPhone 5s") };
MyEnumerator enumerator = new MyEnumerator(phones);
while (enumerator.MoveNext())
{
Phone p = enumerator.Current as Phone;
show(p.Name);
} show("-----------IEnumerable------------");
Phone phoneList = new Phone(phones);
foreach (Phone p in phoneList)
{
show(p.Name);
}
show("-----------IEnumerable<T>------------");
PhonePackage<Phone> phonePackage = new PhonePackage<Phone>();
phonePackage.Add(new Phone("iPhone 7s"));
phonePackage.Add(new Phone("iPhone 6s"));
phonePackage.Add(new Phone("iPhone 5s"));
foreach (Phone p in phonePackage)
{
show(p.Name);
}
Console.ReadKey();
}
static void show(string i)
{
Console.WriteLine(i);
}
结果显示:
IEnumerator迭代器接口挺啰嗦的,yield是简化了遍历的语法糖而已。
参考List<T>源码:http://www.projky.com/dotnet/4.5.1/System/Collections/Generic/List.cs.html
C# IEnumerable 和 IEnumerator接口浅析的更多相关文章
- 细说 C# 中的 IEnumerable和IEnumerator接口
我们先思考几个问题: 为什么在foreach中不能修改item的值? 要实现foreach需要满足什么条件? 为什么Linq to Object中要返回IEnumerable? 接下来,先开始我们的正 ...
- IEnumerable和IEnumerator接口
我们先思考几个问题:1.为什么在foreach中不能修改item的值?(IEnumerator的Current为只读)2.要实现foreach需要满足什么条件?(实现IEnumerator接口来实现的 ...
- IEnumerable、IEnumerator接口(如何增加迭代器功能)
IEnumerable.IEnumerator接口封装了迭代器功能,有了它,我们不需要将内部集合暴露出去,外界只需要访问我的迭代器接口方法即可遍历数据. 在C#中,使用foreach语句来遍历集合.f ...
- 迭代器学习之一:使用IEnumerable和IEnumerator接口
写博客是检验我学习的成果之一以及自我总结的一种方式,以后会经常利用这种方式进行技术交流和自我总结,其中认识不深难免会有错误,但是一直懂得不懂就问,不懂就学的道理! 1.首先看一个简单的列子 , , , ...
- C#基础知识系列九(对IEnumerable和IEnumerator接口的糊涂认识)
前言 IEnumerable.IEnumerator到现在为止对这两个接口还是不太理解,不理解但是自己总是想着试着要搞明白,毕竟自己用的少,所以在此先记录一下.以备自己日后可以来翻查,同时也希望园子里 ...
- C# ~ 从 IEnumerable / IEnumerator 到 IEnumerable<T> / IEnumerator<T> 到 yield
IEnumerable / IEnumerator 首先,IEnumerable / IEnumerator 接口定义如下: public interface IEnumerable /// 可枚举接 ...
- IEnumerable和IEnumerator
概述 IEnumerable和IEnumerator接口存在的意义:用来实现迭代的功能! public interface IEnumerable { IEnumerator GetEnumerato ...
- IEnumerable和IEnumerator 详解 (转)
原文链接:http://blog.csdn.net/byondocean/article/details/6871881 参考链接:http://www.cnblogs.com/hsapphire/a ...
- [转]那些年我还不懂:IList,ICollection,IEnumerable,IEnumerator,IQueryable
1.首先看一个简单的例子 int[] myArray = { 1, 32, 43, 343 }; IEnumerator myie = myArray.GetEnumerator(); myie.Re ...
随机推荐
- dos2unix和unix2dos
dos2unix将windows格式的文件转换为linux格式的文件. unix2dos将linux格式的文件转换为windows格式的文件. dos2unix和unix2dos会转换windows和 ...
- Linux系统zookeeper环境搭建(单机、伪分布式、分布式)
本人现在对zookeeper的环境搭建做一个总结,一般zookeeper的安装部署可以有三种模式,单机模式.伪分布式和分布式,这三种模式在什么时候应用具体看大家的使用场景,如果你只有一台机器且只是想自 ...
- 洛谷 [P3973] 线性代数
最大权闭合子图,神题 这不是线性代数,这是网络流. 我们看见这是一堆矩阵的运算,而且最后变成了一个数,那么我们就想到,把这个矩阵乘法的过程用具体的数字推出来 我们发现,a是一个01矩阵,然后其实就可以 ...
- UOJ Round #1 [数论 | DP 排列]
UOJ Round #1 难度很良心啊! 做出了前两题,第三题看到仙人掌就吓哭了. [UR #1]缩进优化 就是求 \[ \sum_{i=1}^n a_i - (x-1)\sum_{i=1}^n\lf ...
- POJ 2154 Color [Polya 数论]
和上题一样,只考虑旋转等价,只不过颜色和珠子$1e9$ 一样的式子 $\sum\limits_{i=1}^n m^{gcd(i,n)}$ 然后按$gcd$分类,枚举$n$的约数 如果这个也化不出来我莫 ...
- Linux设置DNS地址及清理DNS缓存方法
1.设置DNS地址 编辑vim /etc/resolv.conf 文件. 增加DNS地址:nameserver ip. 2.清理DNS缓存 清理dns缓存: 通过重启nscd服务来达到清理dns缓存的 ...
- Linux 6.4 设置yum 为centOS源
一. 删除Redhat 自带的yum // root 用户执行 rpm -aq|grep yum|xargs rpm -e --nodeps 二 .下载CentOS 的 yum 安装文件 wget h ...
- 基于Java SE集合的充值管理系统
1.功能分析 ①管理员管理 注册.登录.退出 ②注册一卡通:记录相应信息. ③充值管理:对一卡通账户进行充值,查询,修改. 2.技术要求 ①Java 基础知识 + 集合类(模拟数据库). ②数据用对象 ...
- volatile简要解析
在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写.这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值 ...
- js使用defineProperty的一些坑
var p2={ }; Object.defineProperty(p2,"gs",{ get:function () { return this.gs; }, set:funct ...