なんとなく 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 はコレクション型と呼ばれます。
8.8.4 foreach ステートメント
- "構造体型"、"クラス型"、または "インターフェイス型" を返すシグネチャ GetEnumerator() を持つ public インスタンス メソッドが C に含まれること。このメソッドは次の説明で E と表記されています。
- シグネチャ MoveNext() および戻り値の型 bool を持つ public インスタンス メソッドが E に含まれること。
- 現在の値を読み取ることができる、Current という名前の public インスタンス プロパティが E に含まれること。このプロパティの型は、コレクション型の要素型と呼ばれます。
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
で列挙子をとるから大丈夫だった。
(このサンプルコードは改行してからの中括弧じゃないのはなんでなの?そういうルール推奨じゃなかったの?)