建议18:foreach不能代替for

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

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

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

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

            for (int i = ; 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个建议》陆敏技

最新文章

  1. mysql 存储过程在批处理数据中的应用
  2. java 堆栈分析3
  3. Httplistener Access Denied
  4. Android 使用SoundPool播放音效
  5. 机器学习公开课笔记(5):神经网络(Neural Network)——学习
  6. 快速排序模板qsort(转载)
  7. EasyUI combotree 使用技巧
  8. python随笔
  9. 6款基于SVG的HTML5应用和动画
  10. POJ 1661 Help Jimmy
  11. Java基础知识学习
  12. Spring+EhCache缓存实例(详细讲解+源码下载)(转)
  13. 三种方式设置特定设备UWP XAML view
  14. iOS 11更新后以及iPhone X推出后工程中遇到的问题及适配
  15. Java集合中的Map接口
  16. 使用Chrome浏览器访问谷歌和维基百科
  17. SourInsight4 配置视野内引用高亮
  18. Windows下TeX Live + Sublime Text 3 + Sumatra PDF配置
  19. Windows 2008 R2上配置IIS7或IIS7.5中的URLRewrite(URL重写)实例
  20. Ubuntu 16.04 LTS sublime text 3 解决不能输入中文

热门文章

  1. java的日期加减
  2. lua语法基本
  3. EM64T和64位是不是一个概念啊?他们有什么区别啊,怎么区分啊?
  4. 实战 TestNG 监听器
  5. 杂项-公司-百科:伯克希尔&#183;哈撒韦-un
  6. CentOS7 日期时间设置
  7. socket通讯实例与TCP/UDP的区别
  8. 前端学习笔记一:什么是W3C?
  9. [原创]Mybatis特殊值Enum类型转换器-ValuedEnumTypeHandler
  10. Android 4.4 外置卡