透过IL看C# (3)——foreach语句

透过IL看C# (3)
foreach语句

摘要:foreach语句是C#中一种重要的循环语句,用于遍历一个数组或对象集合中的每一个元素。这一篇文章介绍了在面对数组、IEnumerable接口和自定义类型时,编译器为foreach语句生成的IL代码。

foreach语句是C#中一种重要的循环语句,用于遍历一个数组或对象集合中的每一个元素。foreach语句的基本形式如下:

代码1 - foreach语句的基本形式

foreach语句的作用就是,对于代码1中的[集合],每次循环都取出一个元素放在[变量]中,然后执行一次[语句或语句块]。注意,在[语句或语句块]中,[变量]是只读的。也就是说,只能访问[变量]的值,而不能为其赋值。

在foreach语句中使用数组

数组是最简单的集合类型,也最常用在foreach语句中。代码2给出了一个简单的foreach循环。

代码2 - 使用数组的foreach循环

代码3给出了使用ILDASM工具得到的编译器为上述代码生成的IL。其中我用C#代码给出了注释,便于查看。

代码3 - 代码2对应的IL

代码3中出现的中间变量V_2(在代码2中没有对应的变量)和IL_0013处的“V_2++”暴露了这段代码的结构特征——和for语句是一样的。

由此,可以得到第一个结论——对于使用数组的foreach语句来说,编译器会将其翻译为和for语句类似的IL代码。但要注意,两者之间还是有区别的,前文已经提到过,在foreach语句中数组元素是只读的。

在foreach语句中使用一般集合(ICollection)

接下来,我们尝试在foreach循环中使用一般性的集合,这里用到的是一个实现了ICollection<int>接口的参数。请参见代码4。

代码4 - 使用ICollection的foreach循环

代码5是代码4对应的IL代码,同样给出了C#语句的注释。

代码5 - 代码4对应的IL

可以看到,在方法的最开始,即调用ICollection类型的GetEnumerable()方法得到了一个枚举器(IEnumerator)。

从IL_0009到IL_0023(.try块内的部分)实际上是一个while循环结构。只不过在用C#写程序时,是在while语句顶部进行条件判断的,而对应的IL则是在整个循环的底部进行判断。

由此,我们又得到了第二个结论——对于一般性的集合,foreach语句会首先会将集合转变为IEnumerable接口(ICollection接口继承自IEnumerable),然后取得IEnumerator对象,之后通过while循环进行遍历。

同时,也纠正了前文的一个说法,那就是,foreach语句不仅适用于“集合”,只要是“可枚举”的对象(实现了IEnumerable接口),都可以使用foreach进行遍历。

在foreach语句中使用自定义类型

让我们再进一步,如果我们自己写一个类型,能否将其对象作为“集合”用foreach进行遍历呢?

通过上面的结论2可以推测,只要我们自己写的类型实现了IEnumerable接口(包括IEnumerable泛型接口),就应该可以。

答案也是肯定的。

不过,事实是,即便不实现IEnumerable接口,只要提供了签名是public IEnumerator GetEnumerator()的方法,一个自定义类型的对象依然可以通过foreach进行遍历。例如代码6给出的类型。

代码6 - 可以在foreach语句中使用的自定义类型

需要注意的是,这个GetEnumerator方法,必须是public并且不带参数,其返回值必须是IEnumerator或IEnumerator<T>。这个限制,其实和实现IEnumerable接口是一样的。所以,大家只要了解到这一特性就可以了,如果真的需要使用foreach语句遍历自己的类型,还是实现IEnumerable接口为上。

下面是一个使用自定义类型的foreach语句的示例。编译器为其生成的IL与代码5类似,这里就不再罗列了,读者可以自己用ILDasm看一看。

代码7 - 使用自定义类型的foreach语句

小结

本文介绍了在foreach语句中使用数组、一般性集合和自定义类型的情形。

在使用数组时,foreach和for是等价的(但foreach只能进行只读的遍历),所以我们无需担心获取和使用枚举器时的开销。

在使用一般集合和自定义类型时,实际上是通过调用GetEnumerator方法获取了枚举器之后,通过while循环进行遍历的。虽然一个自定义类型只要实现了具有特定签名的GetEnumerator方法,就可以结合foreach语句使用,但还是建议在开发这样的类型时实现IEnumerable接口。

返回目录:透过IL看C#

posted @ 2009-02-04 01:17  Anders Liu  阅读(6780)  评论(23编辑  收藏  举报