[Effective C#] 項目11 foreach ループを使用すること

本日の Effective C# は、「項目11 foreach ループを使用すること」です。

まあ、言わずもがなな感じがします。でも、勉強ですからきちんと読んでみます。

ちなみに、プログラムを書くときに大事なことの一つに局所化があると思います。foreach を使うことが局所化と言えるかどうかは分かりませんが、while/for のループに比べると、要素を必ず一つずつ処理しているのが明確になるのが大事だと思います。

while/for を利用するということは、foreach を使えないロジックであることが明確になるのも大事です。

さて、概念的なことは置いておいて、Effective C# では、foreach を使用する理由は、安全性やパフォーマンスを挙げています。

int [] foo = new int[100];
 
// ループ1:
foreach (int i in foo)
    Console.WriteLine(i.ToString());
 
// ループ2:
for (int index = 0; index < foo.Length; index++)
    Console.WriteLine(foo[index].ToString());
 
// ループ3:
int len = foo.Length;
for (int index = 0; index < len; index++)
    Console.WriteLine(foo[index].ToString());

このうち、ループ1 が最善の選択肢、だそうです。(ただし、C#1.0 では遅いらしいが、C#1.1 以降では速い) ループ3 は JIT コンパイラの最適化のせいで、ループ2 より遅いらしいです。(ループ3 では最適化が働かず、配列の境界チェックが動作してしまうため、らいし)

なので、必ず foreach を使えってことらしいです。

んで、理由をまとめると。

  • 常に最善のコードが生成される。
  • 配列のインデックスが 0 以外から開始されることを考慮しなくていい。
  • 多次元配列でも、各要素ごとに必ず処理してくれる。
  • ループ変数が読み取り専用なので、コレクションの要素を置き換えることができない。
  • Array 型から ArrayList 型へ書き換えても動くように、状況に応じて最適なコードが生成される。
  • イテレータが IDisposable インターフェイスを実装していた場合、自動的に Dispose() メソッドを呼び出してくれる。

ちなみに、foreach 系のループを最初に覚えたのは Perl でなのですが、その時は、for/while ループで書けることを別構文でも書ける意味が分かりませんでした。でも、今では foreach ループ以外書きたくないというか。