Linqの遅延評価と処理内容
LinqのIEnumerableの内容は遅延評価される。その内容を生成する処理は、
- その処理が呼び出されたときではなく、
- その内容が使われるときに実行される。
そのため処理の内容によっては、思わぬ結果になることがある。
以下の Person クラス、ProgramクラスのMakePersonsメソッド、SayHelloメソッドを考える。
class Person { public readonly string Name; public Person(string name) { Console.WriteLine("*** " + name + " を作成 ***"); Name = name; } } class Program { . . . . . static IEnumerable<Person> MakePersons(IEnumerable<string> names) { return names.Select((name) => new Person(name)); } static void SayHello(IEnumerable<Person> persons) { foreach (Person person in persons) { Console.WriteLine("こんにちは " + person.Name + " さん"); } } }
次のプログラムのように、MakePersonsメソッドのIEnumerable<Person>型の戻り値personEnumerableを、そのままSayHelloに渡す。
string[] names = { "太郎" }; IEnumerable<Person> personEnumerable = MakePersons(names); SayHello(personEnumerable); SayHello(personEnumerable);
この場合、
- MakePersonsメソッドから戻った時点では、personEnumerableの内容をまだ参照していないので、その処理は実行されない。
- SayHelloメソッドで、personEnumerableを参照するとMakePersonsの内容が実行される。
- もう一度SayHelloメソッドを呼ぶと、もう一度MakePersonsの内容が実行される。
となり、Personのオブジェクトは 2 回作成される。画面の表示は以下のようになる。
*** 太郎 を作成 *** こんにちは 太郎 さん *** 太郎 を作成 *** こんにちは 太郎 さん
次のようにMakePersonsメソッドの戻り値に対してToArray() を実行し、その結果をSayHelloメソッドに渡すと、
Person[] personArray = MakePersons(names).ToArray(); SayHello(personArray); SayHello(personArray);
- MakePersonsメソッドの戻り値をToArray()で参照するので、処理が実行される。
- SayHelloメソッドは、ToArray() で作成した配列の内容を参照する。
となり、 Personのオブジェクトは 1 回だけ作成される。
*** 太郎 を作成 *** こんにちは 太郎 さん こんにちは 太郎 さん