Linqの遅延評価と処理内容

LinqIEnumerableの内容は遅延評価される。その内容を生成する処理は、

  • その処理が呼び出されたときではなく、
  • その内容が使われるときに実行される。

そのため処理の内容によっては、思わぬ結果になることがある。

以下の 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 回だけ作成される。

*** 太郎 を作成 ***
こんにちは 太郎 さん
こんにちは 太郎 さん