建议18:foreach不能代替for

转载 2016年08月31日 13:28:03

建议18:foreach不能代替for

上一个建议中提到了foreach的两个优点:语法更简单,默认调用Dispose方法,所有我们强烈建议在实际的代码编写中更多的使用foreach。但是,该建议也有不适合的场景。

foreach存在一个问题:它不支持循环时对集合进行增删操作。比如,运行下面代码会抛出异常InvalidOperationException:

            List<int> list=new List<int>(){0,1,2,3};
            foreach (int item in list)
            {
                list.Remove(item);
                Console.WriteLine(item);
            }

取而代之的方法是使用for循环

            for (int i = 0; i < list.Count; i++)
            {
                list.Remove(list[i]);
                Console.WriteLine(list[i]);
            }

foreach循环使用了迭代器进行集合的遍历,它在FCL提供的跌代替内部维护了一个对集合版本的控制。那么什么是集合版本?简单来说,其实它就是一个整形的变量,任何对集合的增删操作都会使版本号加1.foreach会调用MoveNext方法来遍历元素,在MoveNext方法内部会进行版本号的检测,一旦检测到版本号有变动,就会抛出InvalidOperationException异常。

如果使用for循环就不会带来这样的问题。for直接使用索引器,它不对集合版本号进行判断,所以不会存在以为集合的变动而带来的异常(当然,超出索引长度这种异常情况除外)。

由于for循环和foreach循环实现上有所不同(前者索引器,后者迭代器),因此关于两者性能上的争议从来没有停止过。但是,即使有争议,双方都承认两者在时间和内存上有损耗,尤其是针对泛型集合时,两者的损耗是在同一个数量级别上的。

以类型List<T>为例,索引器如下所示:

复制代码
[__DynamicallyInvokable]
public T this[int index]
{
    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries"), __DynamicallyInvokable]
    get
    {
        if (index >= this._size)
        {
            ThrowHelper.ThrowArgumentOutOfRangeException();
        }
        return this._items[index];
    }
    [TargetedPatchingOptOut("Performance critical to inline across NGen image boundaries"), __DynamicallyInvokable]
    set
    {
        if (index >= this._size)
        {
            ThrowHelper.ThrowArgumentOutOfRangeException();
        }
        this._items[index] = value;
        this._version++;
    }
}
复制代码

迭代器如下所示:

复制代码
[__DynamicallyInvokable]
public bool MoveNext()
{
    List<T> list = this.list;
    if ((this.version == list._version) && (this.index < list._size))
    {
        this.current = list._items[this.index];
        this.index++;
        return true;
    }
    return this.MoveNextRare();
}
复制代码
复制代码
[__DynamicallyInvokable]
public T Current
{
    [__DynamicallyInvokable, TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
    get
    {
        return this.current;
    }
}
 
复制代码

可以看到,List<T>类内部维护着一个泛型数组:

private T[] _items;

无论是for循环还是foreach循环,内部都是对该数组的访问,而迭代器仅仅是多进行了一次版本检测。事实上,在循环内部,两者生成的IL代码也是差不多的,但是,正如本建议刚开始提到的那样,因为版本检测的缘故,foreach循环并不能代替for循环。

 

 

转自:《编写高质量代码改善C#程序的157个建议》陆敏技

for和foreach那个效率更高?原因是什么?

写这篇文章的原因主要是在开发过程中突然有以下几个疑问,特抽出时间深度探究一下,以加深自身对php的理解。 1、作为一名phper,for和foreach循环遍历几乎每天都在使用,那么这两种遍历方式哪一...
  • bk_guo
  • bk_guo
  • 2017年06月16日 10:47
  • 2512

前端开发者如何编写高质量代码

本篇文章只写了关于html和css的部分内容目的是能让大家感受到编写高质量代码需要的改变及重要性,内容大多来自笔者最近读的《编写高质量代码-web前端开发修炼之道》,感兴趣的小伙伴也可以去看看 web...
  • ST_Pace
  • ST_Pace
  • 2017年08月03日 16:32
  • 227

Javascript 数组循环遍历之forEach

1.  js 数组循环遍历。 数组循环变量,最先想到的就是 for(var i=0;i
  • oscar999
  • oscar999
  • 2013年03月14日 10:37
  • 414141

建议17:多数情况下使用foreach进行循环遍历

建议17:多数情况下使用foreach进行循环遍历 由于本建议涉及集合的遍历,所以在开始讲解本建议之前,我们不妨来设想一下如何对结合进行遍历。假设存在一个数组,其遍历模式可以采用依据索引来进行遍历的...
  • houwc
  • houwc
  • 2016年08月31日 13:22
  • 601

for,foreach,iterator的用法和区别

  • 2017年07月30日 15:18
  • 14KB
  • 下载

从源码角度看for循环和foreach的区别

for循环和foreach的区别

Java流程控制的陷阱——for循环和foreach循环的陷阱

5、for循环的陷阱 5、1 分号的问题 public class SemicolonRight { public static void main(String[] args) { for (...

for 和foreach的区别

1.for循环并不依赖于数组或其他形式的组式数据结构,只是简单的   在调用了代码后,进行一个判断,判断是否要继续。   (非常类似于do..while和while循环) 2.forea...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:建议18:foreach不能代替for
举报原因:
原因补充:

(最多只允许输入30个字)