デリゲートをExpression<>
でラップする場合とそうでない場合の違いは何ですか?
LinQでExpression<Foo>
が頻繁に使用されているようですが、これまでのところ、これとデリゲートを使用することの違いを説明する記事は見つかりませんでした。
例えば。
Func<int, bool> Is42 = (value) => value == 42;
vs.
Expression<Func<int, bool>> Is42 = (value) => value == 42;
ラムダをデリゲートとして保存することで、なんらかのアクションを実行するデリゲートの特定のインスタンスを保存します。変更することはできません。呼び出すだけです。デリゲートを取得すると、それが何を実行し、何を実行しないかを検査するオプションが制限されます。
ラムダを式として格納することにより、デリゲートを表す式ツリーを格納します。パラメータを変更したり、ボディを変更したり、根本的に異なることを実行したりするために操作できます。デリゲートにコンパイルして戻すこともできるため、必要に応じて呼び出すことができます。式を簡単に調べて、そのパラメーターが何であるか、何をどのように実行するかを確認できます。これは、クエリプロバイダーが式を理解して別の言語に変換するために使用できるものです(対応する式ツリーのSQLクエリを記述するなど)。
また、式を使用して動的にデリゲートを作成する方が、コードを発行するよりもずっと簡単です。より高いレベルのコードは、コンパイラーが低レベルになるのではなくコードを表示し、コードをIL命令として表示する方法に非常に類似した式と考えることができます。
したがって、式を使用すると、単純な匿名のデリゲートよりもはるかに多くのことができます。本当に無料ではありませんが、通常のメソッドや匿名のデリゲートと比較してコンパイルされた式を実行すると、パフォーマンスが低下します。しかし、式を使用することによる他の利点はあなたにとって重要かもしれないので、それは問題ではないかもしれません。
Func<>
は単なるデリゲート型です。式は、実行時のデリゲートへのコンパイルがオプションで可能な operations の完全なツリーの実行時表現です。 Linq-to-SQLなどの式パーサーによって解析され、SQLステートメントを生成したり、その他の賢いことを行ったりするのは、このツリーです。ラムダを式タイプに割り当てると、コンパイラーはこの式ツリーと通常のILコードを生成します。 式ツリーの詳細 。
他の答えを説明するために、これらの2つの式をコンパイルし、コンパイラが生成したコードを確認すると、次のようになります。
Func<int, bool> Is42 = (value) => value == 42;
_Func<int, bool> Is42 = new Func<int, bool>((@value) => value == 42);
_
Expression<Func<int, bool>> Is42 = (value) => value == 42;
_ParameterExpression[] parameterExpressionArray;
ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "value");
Expression<Func<int, bool>> Is42 = Expression.Lambda<Func<int, bool>>(Expression.Equal(parameterExpression, Expression.Constant(42, typeof(int))), new ParameterExpression[] { parameterExpression });
_
式ツリー コード内の式内のコードを検査できます。
たとえば、次の式を渡した場合:o => o.Name
、コード内でName
プロパティが式内でアクセスされていることがわかります。
式ツリーノードを表すクラスの派生元となる基本クラスを提供します。
System.Linq.Expressions.BinaryExpression
System.Linq.Expressions.BlockExpression
System.Linq.Expressions.ConditionalExpression
System.Linq.Expressions.ConstantExpression
System.Linq.Expressions.DebugInfoExpression
System.Linq.Expressions.DefaultExpression
System.Linq.Expressions.DynamicExpression
System.Linq.Expressions.GotoExpression
System.Linq.Expressions.IndexExpression
System.Linq.Expressions.InvocationExpression
System.Linq.Expressions.LabelExpression
System.Linq.Expressions.LambdaExpression
System.Linq.Expressions.ListInitExpression
System.Linq.Expressions.LoopExpression
System.Linq.Expressions.MemberExpression
System.Linq.Expressions.MemberInitExpression
System.Linq.Expressions.MethodCallExpression
System.Linq.Expressions.NewArrayExpression
System.Linq.Expressions.NewExpression
System.Linq.Expressions.ParameterExpression
System.Linq.Expressions.RuntimeVariablesExpression
System.Linq.Expressions.SwitchExpression
System.Linq.Expressions.TryExpression
System.Linq.Expressions.TypeBinaryExpression
System.Linq.Expressions.UnaryExpression
http://msdn.Microsoft.com/en-us/library/system.linq.expressions.expression.aspx
式ツリーは、分析でき、たとえばSQLクエリに変換できるlinq式を表します。
他の人が書いたもの(それは完全に正しい)に、実行時に新しいメソッドを作成できるExpression
クラスを介して追加します。いくつかの制限があります。 C#で実行できるすべてのことをExpression
ツリーで実行できるわけではありません(少なくとも.NET 3.5では。NET4.0では、可能なExpression
"型"が多数追加されています)。これの使用は、(たとえば)動的クエリを作成し、それをLINQ-to-SQLに渡すか、ユーザーの入力に基づいていくつかのフィルタリングを行うことです... LINQ-to-SQLと互換性のない動的な方法でしたが、ILコードを直接出力することは非常に困難です:-))