LINQ クエリ演算子の実行形態~遅延実行と即時実行~ の違いを理解するための備忘録


初めに

LINQでよく使うクエリ演算子(WhereとかSelect、CountとかToArray)について、遅延実行と即時実行の
違いを意識して使っていかないとどこかで痛い目を見そうだと思い、簡単な備忘録を残そうと思いました。

参考書籍:実戦で役立つ c#プログラミングのイディオム/定石&パターン

ざっくり概要

よくLINQで使うクエリ演算子の実行形態と説明
引用元:実戦で役立つ c#プログラミングのイディオム/定石&パターン

クエリ演算子 実行形態 説明
Where 遅延実行 条件に基づいて値のシーケンスをフィルター処理する
Select 遅延実行 シーケンスの各要素を新しい型に射影する
Count 即時実行 シーケンス内の要素数を返す
ToArray 即時実行 シーケンスから配列を作成する

※上記以外のクエリ演算子については、ネットから調べるか実戦で役立つ c#プログラミングのイディオム/定石&パターン
を買って見てください!

遅延実行とはどういうものなのを確認する

遅延実行での処理を確認するために、武器の一覧をListに格納し、攻撃力が100以上のものを表示するプログラムで
確かめていく


    //武器のステータスを格納するクラス
    public class weapon_status
    {

        public string Name { get; set; }
        public string Attribute { get; set; }
        public int Status { get; set; }
    }

        static void Main(string[] args)
        {
            //武器のリストを作成
            var weapons = new List<weapon_status>
            {
                new weapon_status{Name = "どうの剣",Attribute = "なし",Status = 10},
                new weapon_status{Name = "はがねの剣",Attribute = "なし",Status = 50},
                new weapon_status{Name = "炎の剣",Attribute = "火",Status = 100},
                new weapon_status{Name = "ドラゴンの剣",Attribute = "竜",Status = 150},

            };

            //攻撃力が100以上のものを、強武器としてStrong_Weaponsに格納
            var Strong_Weapons = weapons.Where(x => x.Status >= 100);

            //Strong_Weaponsを表示する
            //炎の剣 , 火属性
            //ドラゴンの剣, 竜属性 ←のように表示される
            foreach (var item in Strong_Weapons)
            {
                Console.WriteLine($"{item.Name} , {item.Attribute}属性");
            }

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

            //武器のリストに新しく1つの武器を追加
            weapons.Add(new weapon_status { Name = "伝説の剣", Attribute = "光", Status = 255 });

            //Weaponsに武器を追加しただけなので、Strong_Weaponsを表示するときは
            //炎の剣 , 火属性
            //ドラゴンの剣, 竜属性 ←をのように表示される、はず?
            foreach (var item in Strong_Weapons)
            {
                Console.WriteLine($"{item.Name} , {item.Attribute}属性");
            }

            Console.ReadKey();


        }

実行結果

上記の結果になる理由

普通なら、Strong_WeaponsにWhereメソッドを代入した後にList:weaponsに新しい武器を追加しているので、
Strong_Weaponsの出力結果は変わらないと思うのですが、実行して確かめると2つの結果は違って表示されます。

これは、実行形態が【遅延実行】であるWhereメソッドが呼び出されたその時はまだ実行されていないこと、
そしてそのデータ(Strong_Weapons)が本当に必要になった場面(Foreachで要素を取り出したとき)で
クエリが実行されていることが確認できます。

即時実行でクエリを明示的に実行する

クエリを明示的に実行したい場合は実行形態が【即時実行】のものを使うことで可能!


            //攻撃力が100以上のものを、強武器とする
            var Strong_Weapons = weapons.Where(x => x.Status >= 100);

↑を↓のように、【即時実行】であるToArryメソッドを呼び出すことで、その時にクエリが実行され
結果が配列に格納されるため

            //攻撃力が100以上のものを、強武器とする
            var Strong_Weapons = weapons.Where(x => x.Status >= 100).ToArray();

同じ結果を得ることができる

まとめ

・遅延実行:メソッドが呼び出されたタイミングでは実行されず、データが必要になったタイミングで実行される
・即時実行:呼び出されたその瞬間にクエリが実行される
・その時点で明示的にクエリを実行したい場合は即時実行のクエリ演算子を使えばいい

所感

例えばCSVのファイルを取り込んで、いろいろデータを加工しようとしたときに遅延実行・即時実行を考えずに
LINQ使っているといつかやらかしそうだと思ったので備忘録にした次第です。
その時点で明示的にクエリを実行したいのかどうかを意識しておけば大丈夫、ですかね・・・?
とりあえずわかんないけど先輩がそう書いているからTolistやToArrayしちゃえ!っていうよりかはマシになったかなと思います。