C# IEnumerable Foreach 深入理解

阅前提示

本章内容主要针对 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 array = new IEnumerable(){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, IEnumerator,IEnumerable,IEnumerable,IDisposable
 
  

那上面所述的LikeIEnumerable类这部分内容

{
    //dosomething
    yield return 1;
    //dosomething
    yield return 2;
    //dosomething
    yield 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++;
                 //dosomething
                 mCurrent = 1;
                 return true;
             case 1:
                 state++;
                 //dosomething
                 mCurrent = 2;
                 return true;
             case 2:
                 state++;
                 //dosomething
                 mCurrent = 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关键字原理详解


你可能感兴趣的:(C#)