C#のお話。

C#にも他言語同様、コレクション系のクラスで利用できるforeach文という拡張for文機構が存在します。
foreach文の中でカウンタ変数を利用しようというお話です。

foreach 文


イメージ 1
図1 for文とforeach文

foreach文は、Javaなどで言うところの拡張for文にあたり、配列の各要素に対して処理を行いたい場合などに活用できます。
コレクションを始めとする反復可能系型 (IEnumerable互換型) 全般で利用可能なのが特長です。

しかし、foreach文では、for文のようなカウンタ変数 (図1のfor文の i 変数など) が利用できません
foreach文の中でカウントを行いたくなった場合には、もちろんforeach文の前にint型の変数を宣言して、反復セクション内部でインクリメントしていく方法などもありますが、本記事で取り上げるのは、実はLINQを利用することで解決ができてしまうんですというお話です。

匿名型とSelectの利用

C#にはLINQという機能がございます。
反復可能系型 (IEnumerable互換型) 全般で利用可能で、特定の条件に合う項目のみを取り出したり、各項目が有する特定のフィールドのみを取り出したりするのによく使われる機能です。

LINQのうちSelectメソッドと、匿名型を組み合わせることでforeach文の中でカウンタ変数を取れるようになります。

イメージ 4
図2 Selectメソッドのオーバーロード

SelectメソッドにはFunc<T1, T2>型の匿名メソッドを引数として通常利用しますが、実はオーバーロードとして、Func<T1, int, T2>も利用可能です。
このintにはインデックス番号が入ります。

またSelectの引数の匿名メソッドの戻り値には任意の型「T2」が利用可能で、Selectの戻り値はこのT2の反復可能型「IEnumerable<T2>」となるわけですが、このT2には匿名型も利用可能なんです。
このT2の匿名型に、ソースとなっているIEnumerableの要素 (図1で言うところのstring型の値) とインデックス番号を格納してしまえば、foreach文の中でカウントを行うことが可能になるわけです。

まとめ

つまり、foreach文の中でカウントを取ろうと思ったら次の形にしましょう。

イメージ 2
図3 foreach文の中でカウントを行う例

イメージ 3
図4 実行結果


var words = new string[] { "りんご", "ごりら", "らっぱ" };
foreach (var item in words.Select((str, index) => new { str = str, index = index }))
{
Console.WriteLine("{0}: {1}", item.index, item.str);
}


可読性の面で不安に思う方もいらっしゃると思いますが、その際は諦めてカウンタ変数を立てるか、for文を使うほか無いかと思います。
配列やICollection互換などの要素に番号が振られていない型の変数に対して、foreachのような処理を施す際、for文で回そうと思うと同じくLINQの機能であるToArray()などを利用しなくてはならなくなってしまいますが、個人的にはそれよりはこっちのほうが整っているのかなぁと。

好みの問題ですね。