basyura's blog

あしたになったらほんきだす。

foreach のあとの foreach

なんとなく linq で抽出した後に foreach でなにかしら回しながら処理をしたあと、さいど foreach で回すとループで回らないのじゃないかという不安から、ToList() でリストにしてから foreach してしまうことがある。

using System;
using System.Linq;
using System.Collections.Generic;
using System.Collections;

class Class2 {
    static void Main(string[] args) {
        // なにかしらのリスト
        var list = new List<string>(){ "a", "b", "c", "d", "e", "f", "g" };
        // linq でゴニョゴニョ
        var emu = list.Where(v => true);
        // ぐるぐる回す
        foreach(var v in emu)
        {
            Console.WriteLine(v);
        }

        Console.WriteLine("-----------------------------");

        // もう一度まわす
        foreach(var v in emu)
        {
            Console.WriteLine(v);
        }
    }
}

上のコードで言うと、//もう一度まわす が回らずに終わってしまうのじゃないかなという漠然とした不安。List にしとけば大丈夫そうという漠然とした安心感。

ちょっと言語仕様を検索してみる。

System.Collections.IEnumerable インターフェイスを実装するか、または次の基準のすべてを満たすことでコレクション パターンを実装する場合、型 C はコレクション型と呼ばれます。

  • "構造体型"、"クラス型"、または "インターフェイス型" を返すシグネチャ GetEnumerator() を持つ public インスタンス メソッドが C に含まれること。このメソッドは次の説明で E と表記されています。
  • シグネチャ MoveNext() および戻り値の型 bool を持つ public インスタンス メソッドが E に含まれること。
  • 現在の値を読み取ることができる、Current という名前の public インスタンス プロパティが E に含まれること。このプロパティの型は、コレクション型の要素型と呼ばれます。
8.8.4 foreach ステートメント

collection 式の型がコレクション パターンを実装する場合、foreach ステートメントの展開は次のようになります。

E enumerator = (collection).GetEnumerator();
try {
   while (enumerator.MoveNext()) {
      ElementType element = (ElementType)enumerator.Current;
      statement;
   }
}
finally {
   IDisposable disposable = enumerator as System.IDisposable;
   if (disposable != null) disposable.Dispose();
}
8.8.4 foreach ステートメント

ということで、foreach するたびに GetEnumerator で列挙子をとるから大丈夫だった。

(このサンプルコードは改行してからの中括弧じゃないのはなんでなの?そういうルール推奨じゃなかったの?)