认识foreach

  初学者往往不明白为什么c#里有for来处理循环了,还要提供一个foreach。难道仅仅是因为让语法简单明了,提高那么点性能?当然不是那么简单,foreach的设计有他独到之处。foreach和for的循环方式不太一样,这里不再赘述,如果你理解循环和迭代的不同之处,那么一句话就可以区分两者的区别了,for是循环,foreach是迭代。

  要了解foreach的独到之处一定要了解yield,我们看语法就能明白这两者肯定是有联系的,foreach循环的对象必须是实现了IEnumerable或IEnumerable<T>的集合,而yield正是使用在迭代器中的,迭代器返回类型必须是 IEnumerable、 IEnumerable< T> 、 IEnumerator或 IEnumerator< T> 。也许你有点晕,主要是这个迭代器是个什么鬼,不了解也没关系,你只要知道这几个接口都是为迭代器而生,foreach就是基于迭代器进行迭代的。我们来看几个简单的代码你就明白了。首先我们写一个方法用for循环简单的生成一个斐波那契数列,只取前5个数字。

  private static int[] Fibonacci()
  {
    int[] fibNumbers = new int[5];
    fibNumbers[0] = 1;
    fibNumbers[1] = 1;
    for (int i=2; i<5; i++)
    {
      fibNumbers[i] = fibNumbers[i-1] + fibNumbers[i-2];
    }
    return fibNumbers;
  }

然后循环输出该数列

    var fibNums = Fibonacci();
    for (int i=0; i<fibNums.Length; i++)
    {
      Console.WriteLine(fibNums[i]);
    }

上面的代码有一个问题,就是我会执行2次循环,如果外面又套了几层,那就会循环多次,而且数量大了后,那就很影响性能了。foreach的独到之处就在于让他只循环一次,当然得配合yield。修改生成数列代码

  private static IEnumerable<int> Fibonacci()
  {
    int[] fibNumbers = new int[5];
    fibNumbers[0] = 1;
    yield return 1;
    fibNumbers[1] = 1;
    yield return 1;
    for (int i=2; i<5; i++)
    {
      fibNumbers[i] = fibNumbers[i-1] + fibNumbers[i-2];
      yield return fibNumbers[i];
    }
  }

修改输出代码为foreach

    foreach (var i in Fibonacci())
    {
      Console.WriteLine(i);
    }

如果调用Fibonacci方法后又调用了ToArray或ToList再执行foreach那就前功尽弃了,调用ToArray或ToList会执行迭代器做一遍循环生成数组或集合,再做foreach那又会对数组或集合再做一次循环,又变2次循环了。所以我们在碰到返回IEnumerable接口的方法时不要急着ToArray或ToList,还有也不要随意Count(),那都会执行一遍循环从而影响性能。最常见的就是EF中获取集合返回的都是IEnumerable,如果你ToList了再做循环处理数据那就得不偿失了。

猜你喜欢

转载自www.cnblogs.com/tigeer/p/9766907.html