初学C#的时候,老是被IEnumerable、IEnumerator、ICollection等这样的接口弄的糊里糊涂,我觉得有必要切底的弄清楚IEnumerable和IEnumerator的本质。

下面我们先看IEnumerable和IEnumerator两个接口的语法定义。其实IEnumerable接口是非常的简单,只包含一个抽象的方法GetEnumerator(),它返回一个可用于循环访问集合的IEnumerator对象。IEnumerator对象有什么呢?它是一个真正的集合访问器,没有它,就不能使用foreach语句遍历集合或数组,因为只有IEnumerator对象才能访问集合中的项,假如连集合中的项都访问不了,那么进行集合的循环遍历是不可能的事情了。那么让我们看看IEnumerator接口有定义了什么东西。看下图我们知道IEnumerator接口定义了一个Current属性,MoveNext和Reset两个方法,这是多么的简约。既然IEnumerator对象时一个访问器,那至少应该有一个Current属性,来获取当前集合中的项吧。

MoveNext方法只是将游标的内部位置向前移动(就是移到一下个元素而已),要想进行循环遍历,不向前移动一下怎么行呢?

详细讲解:

说到IEnumerable总是会和IEnumerator、foreach联系在一起。

C# 支持关键字foreach,允许我们遍历任何数组类型的内容:

//遍历数组的项

int[] myArrayOfInts = {10,20,30,40};

foreach(int i in my myArrayOfInts)

{

Console.WirteLine(i);

}

虽然看上去只有数组才可以使用这个结构,其实任何支持GetEnumerator()方法的类型都可以通过foreach结构进行运算。

     public class Garage
{
Car[] carArray = new Car[]; //在Garage中定义一个Car类型的数组carArray,其实carArray在这里的本质是一个数组字段 //启动时填充一些Car对象
public Garage()
{
//为数组字段赋值
carArray[] = new Car("Rusty", );
carArray[] = new Car("Clunker", );
carArray[] = new Car("Zippy", );
carArray[] = new Car("Fred", );
}
}

理想情况下,与数据值数组一样,使用foreach构造迭代Garage对象中的每一个子项比较方便:

  //这看起来好像是可行的
class Program
{
static void Main(string[] args)
{
Console.WriteLine("*********Fun with IEnumberable/IEnumerator************\n");
Garage carLot = new Garage(); //交出集合中的每一Car对象吗
foreach (Car c in carLot)
{
Console.WriteLine("{0} is going {1} MPH", c.CarName, c.CurrentSpeed);
} Console.ReadLine();
}
}

让人沮丧的是,编译器通知我们Garage类没有实现名为GetEnumerator()的方法(显然用foreach遍历Garage对象是不可能的事情,因为Garage类没有实现GetEnumerator()方法,Garage对象就不可能返回一个IEnumerator对象,没有IEnumerator对象,就不可能调用方法MoveNext(),调用不了MoveNext,就不可能循环的了)。这个方法是有隐藏在System.collections命名空间中的IEnumerable接口定义的。(特别注意,其实我们循环遍历的都是对象而不是类,只是这个对象是一个集合对象

支持这种行为的类或结构实际上是宣告它们向调用者公开所包含的子项:

//这个接口告知调方对象的子项可以枚举

public interface IEnumerable

{

IEnumerator GetEnumerator();

}

可以看到,GetEnumerator方法返回对另一个接口System.Collections.IEnumerator的引用。这个接口提供了基础设施,调用方可以用来移动IEnumerable兼容容器包含的内部对象。

//这个接口允许调用方获取一个容器的子项

public interface IEnumerator

{

bool MoveNext();             //将游标的内部位置向前移动

object Current{get;}       //获取当前的项(只读属性)

void Reset();                 //将游标重置到第一个成员前面

}

所以,要想Garage类也可以使用foreach遍历其中的项,那我们就要修改Garage类型使之支持这些接口,可以手工实现每一个方法,不过这得花费不少功夫。虽然自己开发GetEnumerator()、MoveNext()、Current和Reset()也没有问题,但有一个更简单的办法。因为System.Array类型和其他许多类型(如List)已经实现了IEnumerable和IEnumerator接口,你可以简单委托请求到System.Array,如下所示:

 namespace MyCarIEnumerator
{
public class Garage:IEnumerable
{
Car[] carArray = new Car[]; //启动时填充一些Car对象
public Garage()
{
carArray[] = new Car("Rusty", );
carArray[] = new Car("Clunker", );
carArray[] = new Car("Zippy", );
carArray[] = new Car("Fred", );
}
public IEnumerator GetEnumerator()
{
return this.carArray.GetEnumerator();
}
}
}
//修改Garage类型之后,就可以在C#foreach结构中安全使用该类型了。
 //除此之外,GetEnumerator()被定义为公开的,对象用户可以与IEnumerator类型交互:
namespace MyCarIEnumerator
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("*********Fun with IEnumberable/IEnumerator************\n");
Garage carLot = new Garage(); //交出集合中的每一Car对象吗
foreach (Car c in carLot) //之所以遍历carLot,是因为carLot.GetEnumerator()返回的项时Car类型,这个十分重要
{
Console.WriteLine("{0} is going {1} MPH", c.CarName, c.CurrentSpeed);
} Console.WriteLine("GetEnumerator被定义为公开的,对象用户可以与IEnumerator类型交互,下面的结果与上面是一致的");
//手动与IEnumerator协作
IEnumerator i = carLot.GetEnumerator();
while (i.MoveNext())
{
Car myCar = (Car)i.Current;
Console.WriteLine("{0} is going {1} MPH", myCar.CarName, myCar.CurrentSpeed);
}
Console.ReadLine();
}
}
}

下面我们来看看手工实现IEnumberable接口和IEnumerator接口中的方法:

 namespace ForeachTestCase
{
//继承IEnumerable接口,其实也可以不继承这个接口,只要类里面含有返回IEnumberator引用的GetEnumerator()方法即可
class ForeachTest:IEnumerable {
private string[] elements; //装载字符串的数组
private int ctr = ; //数组的下标计数器 /// <summary>
/// 初始化的字符串
/// </summary>
/// <param name="initialStrings"></param>
ForeachTest(params string[] initialStrings)
{
//为字符串分配内存空间
elements = new String[];
//复制传递给构造方法的字符串
foreach (string s in initialStrings)
{
elements[ctr++] = s;
}
} /// <summary>
/// 构造函数
/// </summary>
/// <param name="source">初始化的字符串</param>
/// <param name="delimiters">分隔符,可以是一个或多个字符分隔</param>
ForeachTest(string initialStrings, char[] delimiters)
{
elements = initialStrings.Split(delimiters);
} //实现接口中得方法
public IEnumerator GetEnumerator()
{
return new ForeachTestEnumerator(this);
} private class ForeachTestEnumerator : IEnumerator
{
private int position = -;
private ForeachTest t;
public ForeachTestEnumerator(ForeachTest t)
{
this.t = t;
} #region 实现接口 public object Current
{
get
{
return t.elements[position];
}
} public bool MoveNext()
{
if (position < t.elements.Length - )
{
position++;
return true;
}
else
{
return false;
}
} public void Reset()
{
position = -;
} #endregion
}
static void Main(string[] args)
{
// ForeachTest f = new ForeachTest("This is a sample sentence.", new char[] { ' ', '-' });
ForeachTest f = new ForeachTest("This", "is", "a", "sample", "sentence.");
foreach (string item in f)
{
System.Console.WriteLine(item);
}
Console.ReadKey();
}
}
}

IEnumerable<T>接口

实现了IEnmerable<T>接口的集合,是强类型的。它为子对象的迭代提供类型更加安全的方式。

  public  class ListBoxTest:IEnumerable<String>
{
private string[] strings;
private int ctr = ; #region IEnumerable<string> 成员
//可枚举的类可以返回枚举
public IEnumerator<string> GetEnumerator()
{
foreach (string s in strings)
{
yield return s;
}
} #endregion #region IEnumerable 成员
//显式实现接口
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
} #endregion //用字符串初始化列表框
public ListBoxTest(params string[] initialStrings)
{
//为字符串分配内存空间
strings = new String[];
//复制传递给构造方法的字符串
foreach (string s in initialStrings)
{
strings[ctr++] = s;
}
} //在列表框最后添加一个字符串
public void Add(string theString)
{
strings[ctr] = theString;
ctr++;
} //允许数组式的访问
public string this[int index]
{
get {
if (index < || index >= strings.Length)
{
//处理不良索引
}
return strings[index];
}
set {
strings[index] = value;
}
} //发布拥有的字符串数
public int GetNumEntries()
{
return ctr;
}
}
   class Program
{
static void Main(string[] args)
{
//创建一个新的列表框并初始化
ListBoxTest lbt = new ListBoxTest("Hello", "World"); //添加新的字符串
lbt.Add("Who");
lbt.Add("Is");
lbt.Add("Douglas");
lbt.Add("Adams"); //测试访问
string subst = "Universe";
lbt[] = subst; //访问所有的字符串
foreach (string s in lbt)
{
Console.WriteLine("Value:{0}", s);
}
Console.ReadKey();
}
}

综上所述,一个类型是否支持foreach遍历,必须满足下面条件:

方案1:让这个类实现IEnumerable接口

方案2:这个类有一个public的GetEnumerator的实例方法,并且返回类型中有public 的bool MoveNext()实例方法和public的Current实例属性。

IEnumerable和IEnumerator 详解的更多相关文章

  1. IEnumerable和IEnumerator 详解 分类: C# 2014-12-05 11:47 18人阅读 评论(0) 收藏

    原:<div class="article_title"> <span class="ico ico_type_Original">&l ...

  2. IEnumerable和IEnumerator 详解 (转)

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

  3. IEnumerable和IEnumerator 详解 【转】

    初学C#的时候,老是被IEnumerable.IEnumerator.ICollection等这样的接口弄的糊里糊涂,我觉得有必要切底的弄清楚IEnumerable和IEnumerator的本质. 下 ...

  4. 转载 IEnumerable和IEnumerator 详解

    初学C#的时候,老是被IEnumerable.IEnumerator.ICollection等这样的接口弄的糊里糊涂,我觉得有必要切底的弄清楚IEnumerable和IEnumerator的本质. 下 ...

  5. IEnumerable和IEnumerator详解

    引言 IEnumerable是可枚举的所有非泛型集合的基接口, IEnumerable包含一个方法GetEnumerator(),该方法返回一个IEnumerator:IEnumerator提供通过C ...

  6. IEnumerable 使用foreach 详解

    自己实现迭代器 yield的使用 怎样高性能的随机取IEnumerable中的值 我们先思考几个问题: 为什么在foreach中不能修改item的值? 要实现foreach需要满足什么条件? 为什么L ...

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

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

  8. IEnumerator和IEnumerable详解

    IEnumerator和IEnumerable 从名字常来看,IEnumerator是枚举器的意思,IEnumerable是可枚举的意思. 了解了两个接口代表的含义后,接着看源码: IEnumerat ...

  9. IEnumerable<T> 接口和GetEnumerator 详解

    IEnumerable<T> 接口 .NET Framework 4.6 and 4.5   公开枚举数,该枚举数支持在指定类型的集合上进行简单迭代. 若要浏览此类型的.NET Frame ...

随机推荐

  1. 分布式搜索Elasticsearch——QueryBuilders.matchPhrasePrefixQuery

    注:该文项目基础为分布式搜索Elasticsearch——项目过程(一)和分布式搜索Elasticsearch——项目过程(二),项目骨架可至这里下载. ES源代码中对matchPhrasePrefi ...

  2. nyist 737 相邻石子合并问题

    http://acm.nyist.net/JudgeOnline/problem.php?pid=737 动态规划状态方程: dp[i][j]=d[i][k]+dp[k+1][j]+(sum[k]-s ...

  3. Android 签名(2)签名知识要点

    要点 1) 所有的应用程序都必须有数字证书,Android系统不会安装一个没有数字证书的应用程序 2) Android程序包使用的数字证书可以是自签名的,不需要一个权威的数字证书机构签名认证 3) 如 ...

  4. [Unity3d]小地图的制作

    继续今天的学习心得,unity中小地图的制作,实现了小地图中红色小箭头代表场景中的主角,然后人物方向的转变,小地图中箭头也随之改变方向. 效果图     右上角就是小地图,上面有个红色小箭头就是代表主 ...

  5. 读取Excel任务列表并显示在Outlook日历上

    前几天,公司发了一个任务安排,时间不固定,但要求准时到,为了给自己加一个提醒,也为了回顾一下以前的技术,特做了一个Demo. 读取Excel就不多说了,代码很简单,但支持老版本Excel和的版本Exc ...

  6. geetoo编译安装

    关于Gentoo发行版的介绍请看:全球最受欢迎的十大Linux发行版(图) Host机环境:Win2008 + VMware 7.1 下载安装包 下载安装 CD 和 stage3 包: http:// ...

  7. [scu 4423] Necklace

    4423: Necklace Description baihacker bought a necklace for his wife on their wedding anniversary. A ...

  8. java高并发,如何解决,什么方式解决

    之前我将高并发的解决方法误认为是线程或者是队列可以解决,因为高并发的时候是有很多用户在访问,导致出现系统数据不正确.丢失数据现象,所以想到 的是用队列解决,其实队列解决的方式也可以处理,比如我们在竞拍 ...

  9. jQuery修改操作css属性实现方法

    在jquery中我们要动态的修改css属性我们只要使用css()方法就可以实现了,下面我来给各位同学详细介绍介绍. css()方法在使用上具有多样性,我们先来了解css()方法基本知识. css() ...

  10. 用Apache Kafka构建流数据平台

    近来,有许多关于“流处理”和“事件数据”的讨论,它们往往都与像Kafka.Storm或Samza这样的技术相关.但并不是每个人都知道如何将这种技术引入他们自己的技术栈.于是,Confluent联合创始 ...