zoukankan      html  css  js  c++  java
  • 编写高质量代码改善C#程序的157个建议——建议18:foreach不能代替for

    建议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个建议》陆敏技

  • 相关阅读:
    【前端_js】前端跨网络异步获取资源——fetch()
    【前端_React】React小书
    【前端_js】JQuery DataTables插件的使用
    【前端_js】解决ajax跨域请求数据
    event.srcElement在火狐(FireFox)下的兼容问题。搜索框获得焦点时默认文字变化
    ASP.NET MVC 上传大文件时404
    使用Zen coding高效编写html代码
    CSS 去除列表项li前面的小圆点
    谈谈CSS的布局,display、position、float
    JS引用类型之——RegExp
  • 原文地址:https://www.cnblogs.com/jesselzj/p/4728594.html
Copyright © 2011-2022 走看看