阅前提示
本章内容主要针对 C# 中的IEnumerable及其相关内容做详细的解释。
你真的了解Foreach的本质是什么吗?你对yield关键有多少了解呢?希望这篇文章可以让你更清楚的认识你常常会使用的IEnumerable。
适合人群:对C#一定使用基础
阅读方式:浏览
正文
Foreach
这是我们除了for 循环之外可能用到最多的循环语句,它写起来比for循环要舒服而且更易于去理解。
但有时候你会不经意的被它简单的外表所迷惑而造成一些错误(比如在foreach中修改列表的值)。接下来我们会深入了解一下foreach的具体实现,当清楚了解之后我们的使用将会更加得心应手。
CIL下的数组遍历
//C#int[] array = new int[]{1,2,3,4,5};foreach(int item in array){...}//CIL 类似生成如下int[] tempArray;int[] array = new int[]{1,2,3,4,5}tempArray = array;for(int counter = 0;(counter < tempArray.Length);counter++){int item = tempArray[counter];...}
能这样变化是因为数组满足 固定长度 和 索引操作[]
由此也可以看出来,Foreach时不要修改集合
IEnumerable
对于不满足上述所说的条件,就需要实现IEnumerable接口
IEnumerable/IEnumerable< T > 是.NET实现集合的一个关键。集合的本质其实就是一个类,并且最起码实现了**IEnumerable/IEnumerable< T >**所规定的方法。
IEnumerable使类成为集合
Interface IEnumerable{public System.Collections.IEnumerator GetEnumerator (); //这个里面具体实现了 yield return 机制}Interface IEnumerator{public object Current{get;}public bool MoveNext();public void Reset();}
IEnumerable的foreach
//C#IEnumerable<int> array = new IEnumerable<int>(){1,2,3,4,5};foreach(int item in array){...}//CIL 类似生成如下....IEnumerator ator = array.GetEnumerator()while(ator.MoveNext()){ator = array.GetEnumerator()int item = array.Current;...}
没有IEnumerable的foreach
C#编译器不要求一定要实现IEnumerable/IEnumerable< T >才能用foreach对数据类型进行迭代。实际上,编译器采取了一种**“看起来像”**的名称查找方式,即 只要查找到其含有 GetEnumerator()方法,这个方法返回包含Current属性和MoveNext()方法的一个类型,那就可以用foreach
//建立一个有GetEnumerator的类public class LikeIEnumerable{public IEnumerator GetEnumerator(){yield return 1;yield return 2;yield return 3;}}//然后使用foreach进行测试public void Test(){LikeIEnumerable likeEnumerable = new LikeIEnumerable();foreach (var item in likeEnumerable){Log.I(item); //打印出来}}//输出结果://1//2//3
yield
yield关键字是一种语法糖,实际上还是通过实现IEnumberable、IEnumberable< T >、IEnumberator和IEnumberator< T >接口来满足迭代功能
IL阶段下面这部分内容实际上会被生成一个新的类来实现IEnumberable、IEnumberable< T >、IEnumberator和IEnumberator< T >
//类似如下public class YieldClass : IEnumerator<object>, IEnumerator,IEnumerable<object>,IEnumerable,IDisposable
那上面所述的LikeIEnumerable类这部分内容
{//dosomethingyield return 1;//dosomethingyield return 2;//dosomethingyield return 3;}
实际上可以被看作
public class LikeYield{int state = 0;private object mCurrent;public object Current{get{return mCurrent;}}public bool MoveNext(){switch (state){case 0:state++;//dosomethingmCurrent = 1;return true;case 1:state++;//dosomethingmCurrent = 2;return true;case 2:state++;//dosomethingmCurrent = 3;return true;default:return false;}}}
LikeIEnumerable的迭代部分
foreach (var item in likeEnumerable){Log.I(item);}
等同于
while(likeYield.MoveNext()){var item = likeYield.Current;Log.I(item);}
更细致的IL代码可以看这篇文章
c# yield关键字原理详解