MRが楽しい

MRやVRについて学習したことを書き残す

LINQのクエリ演算子の即時実行と遅延実行の動作を確認する

本日は LINQ の技術調査枠です。
LINQのクエリ演算子の即時実行と遅延実行の動作を確認して記事にします。

LINQのクエリ演算子の即時実行と遅延実行

LINQのクエリ演算子には以下の2つの実行形態があります。
実行形態はクエリ演算子ごとに定められています。

即時実行

メソッドを呼び出したタイミングでクエリ演算子が実行されます。

遅延実行

データが参照されるタイミングでクエリ演算子が実行されます。
即時実行に比べてメモリ消費が少ないメリットがあります。

動作比較

以下の2つのクエリ演算子を使って即時実行と遅延実行の動作の違いを確認します。

Where 遅延実行 シーケンスを指定の条件でフィルターする
Count 即時実行 シーケンスの要素数を返す

サンプルコード

即時実行と遅延実行の動作の違いを確認する以下のコードを準備しました。
・Program.cs

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

namespace ConsoleLINQTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // クエリ演算子で処理するリストを作成する
            List<int> numbers = new List<int> { 0, 1, 2, 3, 4, 5, };

            // Where演算子で3を超える数値のリストを取得する
            var wherequery = numbers.Where(n => n > 3);

            // Count演算子でリストの要素数を取得する
            var countquery = numbers.Count();

            // クエリ演算子で取得した変数の結果を確認する
            Console.WriteLine("Before -----");
            Console.WriteLine($"Where : {string.Join(",", wherequery.ToArray())}");
            Console.WriteLine($"Count : {countquery.ToString()}");

            // 元リストの最後の要素を削除する
            numbers.Remove(5);

            // 再びクエリ演算子で取得した変数の結果を確認する
            Console.WriteLine("After -----");
            Console.WriteLine($"Where : {string.Join(",", wherequery.ToArray())}");
            Console.WriteLine($"Count : {countquery.ToString()}");
        }
    }
}

本コードを実行すると以下の標準出力が得られます。

Before -----
Where : 4,5
Count : 6
After -----
Where : 4
Count : 6

f:id:bluebirdofoz:20210918231324j:plain

即時実行の Count の内容は元リストを改変した後でも変わりませんが、遅延実行の Where の内容は元リストを改変すると変化しています。
これは遅延実行のクエリ演算子は変数に結果を代入しているわけでなく、データが参照されるタイミングで演算が行われることを表しています。

もし、変数に結果を代入しておきたい場合は以下のような即時実行のクエリ演算子を組み合わせることで代入しておくことができます。

ToArray 即時実行 シーケンスから配列を作成する

・Program.cs

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

namespace ConsoleLINQTest
{
    class Program
    {
        static void Main(string[] args)
        {
            // クエリ演算子で処理するリストを作成する
            List<int> numbers = new List<int> { 0, 1, 2, 3, 4, 5, };

            // Where演算子で3を超える数値のリストを取得する
            var wherequery = numbers.Where(n => n > 3);

            // Where演算子で3を超える数値のリストを取得し、即時実行のToArrayで配列として代入しておく
            var wherearrayquery = numbers.Where(n => n > 3).ToArray();

            // クエリ演算子で取得した変数の結果を確認する
            Console.WriteLine("Before -----");
            Console.WriteLine($"Where : {string.Join(",", wherequery.ToArray())}");
            Console.WriteLine($"WhereToArray : {string.Join(",", wherearrayquery)}");

            // 元リストの最後の要素を削除する
            numbers.Remove(5);

            // 再びクエリ演算子で取得した変数の結果を確認する
            Console.WriteLine("After -----");
            Console.WriteLine($"Where : {string.Join(",", wherequery.ToArray())}");
            Console.WriteLine($"WhereToArray : {string.Join(",", wherearrayquery)}");
        }
    }
}

以下の通り、ToArray で事前に代入を行っておいた変数は元リストを変更しても影響を受けていません。

Before -----
Where : 4,5
WhereToArray : 4,5
After -----
Where : 4
WhereToArray : 4,5

f:id:bluebirdofoz:20210918231440j:plain