匿名メソッドとラムダ、そして LINQ の違い - vol 1
匿名メソッドとラムダの違いを調べるためには IL を見れば良いのだが、IL だとわかりにくいので、Reflector にアセンブリを読み込ませてみる。
但し、Reflector の解釈レベルを .NET 2.0 にする。.NET 3.5 にすると、賢く解析してしまうので意味がない(LINQ は LINQ のままになる)。
匿名メソッド
AdventureWorksDataContext adventureWorks = new AdventureWorksDataContext();
IEnumerable<int> query = adventureWorks.Address.
Where<Address>(delegate (Address a) {return (a.City == "Bothell");}).
Select<Address, int>(delegate (Address address) {return address.AddressID;});
ラムダ、LINQ
ParameterExpression CS$0$0000;
AdventureWorksDataContext adventureWorks = new AdventureWorksDataContext();
IQueryable<int> query = adventureWorks.Address.
Where<Address>(Expression.Lambda<Func<Address, bool>>(
Expression.Equal(Expression.Property(CS$0$0000 = Expression.Parameter(typeof(Address), "address"),
(MethodInfo) methodof(Address.get_City)),
Expression.Constant(
"Bothell", typeof(string)), false, (MethodInfo) methodof(string.op_Equality)), new ParameterExpression[] { CS$0$0000 })).
Select<Address, int>(Expression.Lambda<Func<Address, int>>(
Expression.Property(CS$0$0000 = Expression.Parameter(typeof(Address), "address"),
(MethodInfo) methodof(Address.get_AddressID)), new ParameterExpression[] { CS$0$0000 }));
違いは明らかだ。匿名メソッドの結果は IEnumurable になり、ラムダ、LINQ の結果は IQueryable になり、そして式の表現に Expression クラスを多用している。
ラムダの例が何をしているのかと言うと「式の表現を IL に残す」という事をしている。これが「Expression Tree の構築」だ。
これの何が大事なのか。
最終的に、LINQ から SQL を発行するためには IL を解析しなければならないのだが、比較や加算の生の IL オペコードを解析するのは骨が折れる。
しかし、IL に「式の表現」がそのまま残っていたら解析は容易い。その為に存在するのが「System.Linq.Expressions 名前空間」だ。この名前空間には Expression Tree を構築するためのクラスが存在する。
ラムダによる表現は Expression Tree を構築するための syntax sugar となるのだ。(但し、前回記事のコメントで NyaRuRu さんが述べているように、ラムダが必ず Expression Tree になるというわけではないという事に注意。Expression になりようがない複雑なステートメントは普通のオペコードになる)。
今回の例をまとめると、匿名メソッドの場合では「Address テーブルのデータをデータベースから全て取得」する事しか分からないので、全てのデータを取得してからメモリ上で射影、選択操作を行わなければならず、ラムダの場合では「Address テーブルのデータをデータベースに射影、選択操作を行わせてから取得」する事ができるというわけだ。
匿名メソッドで書いたLINQ to SQL(そんな人がいるのかどうかは疑問だが)が発行する SQL に愕然として「使えないなぁ」と早計しないように、ここで述べた事を脳の端に入れておいても損はないだろう。