温故而知新,可以为师矣,有空经常复习一下基础知识是有必要的,并且能加深理解和记忆。

  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接口浅析的更多相关文章

  1. 细说 C# 中的 IEnumerable和IEnumerator接口

    我们先思考几个问题: 为什么在foreach中不能修改item的值? 要实现foreach需要满足什么条件? 为什么Linq to Object中要返回IEnumerable? 接下来,先开始我们的正 ...

  2. IEnumerable和IEnumerator接口

    我们先思考几个问题:1.为什么在foreach中不能修改item的值?(IEnumerator的Current为只读)2.要实现foreach需要满足什么条件?(实现IEnumerator接口来实现的 ...

  3. IEnumerable、IEnumerator接口(如何增加迭代器功能)

    IEnumerable.IEnumerator接口封装了迭代器功能,有了它,我们不需要将内部集合暴露出去,外界只需要访问我的迭代器接口方法即可遍历数据. 在C#中,使用foreach语句来遍历集合.f ...

  4. 迭代器学习之一:使用IEnumerable和IEnumerator接口

    写博客是检验我学习的成果之一以及自我总结的一种方式,以后会经常利用这种方式进行技术交流和自我总结,其中认识不深难免会有错误,但是一直懂得不懂就问,不懂就学的道理! 1.首先看一个简单的列子 , , , ...

  5. C#基础知识系列九(对IEnumerable和IEnumerator接口的糊涂认识)

    前言 IEnumerable.IEnumerator到现在为止对这两个接口还是不太理解,不理解但是自己总是想着试着要搞明白,毕竟自己用的少,所以在此先记录一下.以备自己日后可以来翻查,同时也希望园子里 ...

  6. C# ~ 从 IEnumerable / IEnumerator 到 IEnumerable<T> / IEnumerator<T> 到 yield

    IEnumerable / IEnumerator 首先,IEnumerable / IEnumerator 接口定义如下: public interface IEnumerable /// 可枚举接 ...

  7. IEnumerable和IEnumerator

    概述 IEnumerable和IEnumerator接口存在的意义:用来实现迭代的功能! public interface IEnumerable { IEnumerator GetEnumerato ...

  8. IEnumerable和IEnumerator 详解 (转)

    原文链接:http://blog.csdn.net/byondocean/article/details/6871881 参考链接:http://www.cnblogs.com/hsapphire/a ...

  9. [转]那些年我还不懂:IList,ICollection,IEnumerable,IEnumerator,IQueryable

    1.首先看一个简单的例子 int[] myArray = { 1, 32, 43, 343 }; IEnumerator myie = myArray.GetEnumerator(); myie.Re ...

随机推荐

  1. 洛谷 [P2762] 太空飞行计划问题

    最大权闭合子图 胡伯涛论文真是个好东西.jpg 求一个有向图的最大权闭合子图,常应用于有先决条件的最优化问题中 将所有正权点与源点相连,容量为点权; 将所有负权点与汇点相连,容量为点权的相反数; 将原 ...

  2. 洛谷 [P2024] 食物链

    并查集 这是一道比较特殊的并查集,开一个三倍的数组, 1-n保存同类,n-n×2保存猎物,n2~n3保存天敌: #include <iostream> #include <cstdi ...

  3. BZOJ 4455: [Zjoi2016]小星星 [容斥原理 树形DP]

    4455: [Zjoi2016]小星星 题意:一个图删掉一些边形成一棵树,告诉你图和树的样子,求让图上的点和树上的点对应起来有多少方案 看了很多题解又想了一段时间,感觉题解都没有很深入,现在大致有了自 ...

  4. 如何解决JavaScript中0.1+0.2不等于0.3

    console.log(0.1+0.2===0.3)// true or false?? 在正常的数学逻辑思维中,0.1+0.2=0.3这个逻辑是正确的,但是在JavaScript中0.1+0.2!= ...

  5. 使用requireJS

    什么是require? require是AMD模块化规范的具体实现. 目前,通行的js模块化规范有两种,CommonJS和AMD. CommonJS和AMD有什么不同呢? CommonJS主要用于服务 ...

  6. Leetcode刷题C#版之 Length of Last Word

    题目: Given a string s consists of upper/lower-case alphabets and empty space characters ' ', return t ...

  7. Python面向对象篇(2)-继承

    在发表本篇随笔的时候,距离上一次发已经有一个多月了,很多朋友私信我为什么不持续更新了,在这里先跟大家说声抱歉.因为年底的工作较为繁重,实在分不出精力,更重要的也是在思考后面进阶的部分要按怎样的顺序写, ...

  8. 一个脚本从git上pull 并更新到服务器

    #/bin/bash cd /src/pid01-beta/ echo "update pid01.." git pull ]; then echo "update pi ...

  9. ansible实践2-拷贝文件或目录

      ansible testhost -m copy -a "src=/etc/ansible  dest=/tmp/ansibletest owner=root group=root mo ...

  10. MysqL应该考虑到的安全策略

    1:使用预处理语句防止SQL注入2:写入数据库的数据要进行特殊字符的转义,比如字符中带单引号和双引号需要在应用层转义,这样为了防止SQL注入3:查询的错误信息不要返回给用户,将错误记录到日志.错误信息 ...