代码改变世界

for VS foreach

2009-11-12 09:32  宗哥  阅读(4142)  评论(17编辑  收藏  举报
背景知识
for 循环重复执行一个语句或语句块,直到指定的表达式计算为 false 值。for 循环对于迭代数组和顺序处理非常方便。
foreach 语句为数组或对象集合中的每个元素重复一个嵌入语句组。foreach 语句用于循环访问集合以获取所需信息,但不应用于更改集合内容以避免产生不可预知的副作用;
迭代器是 C# 2.0 中的新功能。迭代器是方法、get 访问器或运算符,它使您能够在类或结构中支持 foreach 迭代,而不必实现整个 IEnumerable 接口 具体参考:MSDN
for VS foreach
for和foreach都可以对集合和数组的元素进行逐个处理。 关于for和foreach有什么区别那?主要从以下方面进行比较
  • 语法方面
  • 语义方面
  • 性能方面
  • 线程安全
  • 使用foreache的一点体会
网上上有很多关于他们的讨论, 阅读本文之前你可以先参看其他文章: 
语法方面
  • 简洁,方面
    for
    for
    foreach
    List<Car> carlist = new List<Car>();
                
    foreach (Car c in carlist)
                {
                    Console.Write(
    "{0}", c.Weight);
                }
    可以看出,对于集合的遍历, foreach比for简洁。当然,如果只遍历集合的一部分,foreach爱莫能助了。
  • 迭代变量的读写性方面 对于for,迭代变量是可读可写的,foreach是只读的。为什么?

    能使用foreach的集合必须实现IEnumerable接口。Reflector IEnumerable的IL代码如下:

     

    [ComVisible(true), Guid("496B0ABE-CDEE-11d3-88E8-00902754C43A")]
    public interface IEnumerable
    {
        IEnumerator GetEnumerator();
    }

    在接着看 System.Collections.IEnumerator GetEnumerator() 里面的:

     

    [ComVisible(true), Guid("496B0ABF-CDEE-11d3-88E8-00902754C43A")]
    public interface IEnumerator
    {
        
    bool MoveNext();
        
    object Current { get; }
        
    void Reset();
    }

    注意:

     

      object Current { get; }

    是不是很熟悉,只读属性,就解释了为什么foreach迭代变量为只读的了。

     

语义方面
学习开发语言,我觉得语义比语法更重要.学习英语我觉得也是如此。
foreach 传达出来的语义就是对于集合一个一个检索,而for就是一循环结构。
个人觉得foreach 可以嗅出面向对象的味道来。
性能方面
关于性能方面我不能多说什么,你可以自己动手测试。下面是我的一段测试代码,供参考,如果你有更好的性能代码测试,可以写出来.

性能测试比较代码
究竟如何选择,参见:《Effective C#》 - 条款11:优先采用foreach循环语句
C#的foreach语句不仅仅只是do... while或者for循环语句的一个变体。它会为我们的集合产生最好的遍历代码。实际上,foreach语句的定义和.NET框架中的集合接口密切相关。 对于一些特殊的集合类型,C#编译器会产生具有最佳效率的代码。遍历集合时,我们应该使用foreach语句,而非其他的循环构造
线程安全
对于一个集合,在遍历过程中,如果集合发生异动,foreach会发生异常,而for不会。
这个是一个曾经电话面试过我的一个工程师说的,我觉得这句话值得商榷,只是在.Net Framework提供的常见集合(Array,List等)会发生,发生异常代码参考:
Code
这个异常原因可以参见: 在Dictionary使用foreach的注意 如果你自己动手实现迭代器就不会了。代码如下:
Code
对于一个集合,在遍历过程中,如果集合发生异动,foreach会发生异常,而for不会,foreach本身没有错,关键看你如何实现迭代器。

使用foreache的一点体会
  • 使用lock 对于你能掌控的代码,把这个集合变量作为一个临界变量,使用lock加锁,确保代码不会出现“集合已修改;可能无法执行枚举操作。”类似异常。如下代码供参考:
    Code
    注意lock用法。 
  • 使用本地变量拷贝 把这个集合变量拷贝到本地变量,然后对本地变量进行遍历。
    Code
    
;