式ツリー (C# および Visual Basic)

式ツリーには、コードがツリー形式で表示されます。各ノードは 1 つの式に対応しています。たとえば、メソッドの呼び出しや x < y のような二項演算などです。

式ツリーで表されるコードはコンパイルおよび実行できます。 これによって、実行可能なコードの動的な変更、多様なデータベースの LINQ クエリの実行、および動的クエリの作成が可能になります。 LINQ の式ツリーの詳細については、「方法: 式ツリーを使用して動的クエリをビルドする (C# および Visual Basic)」および「チュートリアル : IQueryable LINQ プロバイダーの作成」を参照してください。

また、式ツリーを動的言語ランタイム (DLR: Dynamic Language Runtime) に使用することで、動的言語と .NET Framework 間の相互運用性が実現し、コンパイラ ライターで Microsoft Intermediate language (MSIL) ではなく式ツリーを出力できるようになります。 DLR の詳細については、「動的言語ランタイムの概要」を参照してください。

匿名のラムダ式に基づいて、C# と Visual Basic コンパイラで式ツリーを作成することができます。または、System.Linq.Expressions 名前空間を使用して手動で式ツリーを作成できます。

ラムダ式からの式ツリーの作成

ラムダ式が Expression<TDelegate> 型の変数に割り当てられている場合、コンパイラはラムダ式を表す式ツリーを構築するコードを出力します。

C# および Visual Basic コンパイラは、式形式のラムダ (つまり単一行のラムダ) からのみ式ツリーを生成できます。 ステートメント形式のラムダ (つまり複数行のラムダ) は解決できません。 C# のラムダ式の詳細については、「ラムダ式 (C# プログラミング ガイド)」を参照してください。Visual Basic については、「ラムダ式 (Visual Basic)」を参照してください。

次のコード例では、ラムダ式 num => num < 5 (C#) または Function(num) num < 5 (Visual Basic) を表す式ツリーを C# および Visual Basic コンパイラで作成する方法を示しています。

Dim lambda As Expression(Of Func(Of Integer, Boolean)) =
    Function(num) num < 5
Expression<Func<int, bool>> lambda = num => num < 5;

API による式ツリーの作成

API を使用して式ツリーを作成するには、Expression クラスを使用します。 このクラスには、特定の型を持つ式ツリー ノードを作成する静的ファクトリ メソッドが含まれます。たとえば、変数またはパラメーターを表す ParameterExpression や、メソッドの呼び出しを表す MethodCallExpression などです。 ParameterExpressionMethodCallExpression などの式固有の型も、System.Linq.Expressions 名前空間で定義されます。 これらの型は Expression 抽象型から派生したものです。

次のコード例は、API を使用して、ラムダ式 num => num < 5 (C#) または Function(num) num < 5 (Visual Basic) を表す式ツリーを作成する方法を示しています。


' Import the following namespace to your project: System.Linq.Expressions

' Manually build the expression tree for the lambda expression num => num < 5.
Dim numParam As ParameterExpression = Expression.Parameter(GetType(Integer), "num")
Dim five As ConstantExpression = Expression.Constant(5, GetType(Integer))
Dim numLessThanFive As BinaryExpression = Expression.LessThan(numParam, five)
Dim lambda1 As Expression(Of Func(Of Integer, Boolean)) =
  Expression.Lambda(Of Func(Of Integer, Boolean))(
        numLessThanFive,
        New ParameterExpression() {numParam})

            // Add the following using directive to your code file:
            // using System.Linq.Expressions;

            // Manually build the expression tree for 
            // the lambda expression num => num < 5.
            ParameterExpression numParam = Expression.Parameter(typeof(int), "num");
            ConstantExpression five = Expression.Constant(5, typeof(int));
            BinaryExpression numLessThanFive = Expression.LessThan(numParam, five);
            Expression<Func<int, bool>> lambda1 =
                Expression.Lambda<Func<int, bool>>(
                    numLessThanFive,
                    new ParameterExpression[] { numParam });

.NET Framework 4 の式ツリー API は、割り当て式、制御フロー式 (ループ、条件ブロック)、および try-catch ブロックもサポートしています。 API を使用すると、C# および Visual Basic コンパイラのラムダ式から作成できる式ツリーよりも複雑な式ツリーを作成できます。 次の例は、数値の階乗を計算する式ツリーを作成する方法を示しています。

' Creating a parameter expression.
Dim value As ParameterExpression =
    Expression.Parameter(GetType(Integer), "value")

' Creating an expression to hold a local variable. 
Dim result As ParameterExpression =
    Expression.Parameter(GetType(Integer), "result")

' Creating a label to jump to from a loop.
Dim label As LabelTarget = Expression.Label(GetType(Integer))

' Creating a method body.
Dim block As BlockExpression = Expression.Block(
    New ParameterExpression() {result},
    Expression.Assign(result, Expression.Constant(1)),
    Expression.Loop(
        Expression.IfThenElse(
            Expression.GreaterThan(value, Expression.Constant(1)),
            Expression.MultiplyAssign(result,
                Expression.PostDecrementAssign(value)),
            Expression.Break(label, result)
        ),
        label
    )
)

' Compile an expression tree and return a delegate.
Dim factorial As Integer =
    Expression.Lambda(Of Func(Of Integer, Integer))(block, value).Compile()(5)

Console.WriteLine(factorial)
' Prints 120.
// Creating a parameter expression.
ParameterExpression value = Expression.Parameter(typeof(int), "value");

// Creating an expression to hold a local variable. 
ParameterExpression result = Expression.Parameter(typeof(int), "result");

// Creating a label to jump to from a loop.
LabelTarget label = Expression.Label(typeof(int));

// Creating a method body.
BlockExpression block = Expression.Block(
    // Adding a local variable.
    new[] { result },
    // Assigning a constant to a local variable: result = 1
    Expression.Assign(result, Expression.Constant(1)),
    // Adding a loop.
        Expression.Loop(
    // Adding a conditional block into the loop.
           Expression.IfThenElse(
    // Condition: value > 1
               Expression.GreaterThan(value, Expression.Constant(1)),
    // If true: result *= value --
               Expression.MultiplyAssign(result,
                   Expression.PostDecrementAssign(value)),
    // If false, exit the loop and go to the label.
               Expression.Break(label, result)
           ),
    // Label to jump to.
       label
    )
);

// Compile and execute an expression tree.
int factorial = Expression.Lambda<Func<int, int>>(block, value).Compile()(5);

Console.WriteLine(factorial);
// Prints 120.

詳細については、「Generating Dynamic Methods with Expression Trees in Visual Studio 2010 (Visual Studio 2010 での式ツリーによる動的メソッドの生成)」を参照してください。

式ツリーの解析

次のコード例では、ラムダ式 num => num < 5 (C#) または Function(num) num < 5 (Visual Basic) を表す式ツリーを各部分に分解する方法を示します。


        ' Import the following namespace to your project: System.Linq.Expressions

        ' Create an expression tree.
        Dim exprTree As Expression(Of Func(Of Integer, Boolean)) = Function(num) num < 5

        ' Decompose the expression tree.
        Dim param As ParameterExpression = exprTree.Parameters(0)
        Dim operation As BinaryExpression = exprTree.Body
        Dim left As ParameterExpression = operation.Left
        Dim right As ConstantExpression = operation.Right

        Console.WriteLine(String.Format("Decomposed expression: {0} => {1} {2} {3}",
                          param.Name, left.Name, operation.NodeType, right.Value))

        ' This code produces the following output:
        '
        ' Decomposed expression: num => num LessThan 5


// Add the following using directive to your code file:
// using System.Linq.Expressions;

// Create an expression tree.
Expression<Func<int, bool>> exprTree = num => num < 5;

// Decompose the expression tree.
ParameterExpression param = (ParameterExpression)exprTree.Parameters[0];
BinaryExpression operation = (BinaryExpression)exprTree.Body;
ParameterExpression left = (ParameterExpression)operation.Left;
ConstantExpression right = (ConstantExpression)operation.Right;

Console.WriteLine("Decomposed expression: {0} => {1} {2} {3}",
                  param.Name, left.Name, operation.NodeType, right.Value);

// This code produces the following output:

// Decomposed expression: num => num LessThan 5            

式ツリーの不変性

式ツリーは変更できません。 つまり、式ツリーを変更するには、既存の式ツリーをコピーしてツリー内のノードを置き換えることで、新しい式ツリーを作成する必要があります。 式ツリー ビジタを使用して、既存の式ツリーを走査することができます。 詳細については、「方法: 式ツリーを変更する (C# および Visual Basic)」を参照してください。

式ツリーのコンパイル

Expression<TDelegate> 型に含まれる Compile メソッドにより、式ツリーの表すコードを実行可能なデリゲートにコンパイルします。

次のコード例は、式ツリーをコンパイルして結果のコードを実行する方法を示しています。

' Creating an expression tree.
Dim expr As Expression(Of Func(Of Integer, Boolean)) =
    Function(num) num < 5

' Compiling the expression tree into a delegate.
Dim result As Func(Of Integer, Boolean) = expr.Compile()

' Invoking the delegate and writing the result to the console.
Console.WriteLine(result(4))

' Prints True.

' You can also use simplified syntax
' to compile and run an expression tree.
' The following line can replace two previous statements.
Console.WriteLine(expr.Compile()(4))

' Also prints True.
// Creating an expression tree.
Expression<Func<int, bool>> expr = num => num < 5;

// Compiling the expression tree into a delegate.
Func<int, bool> result = expr.Compile();

// Invoking the delegate and writing the result to the console.
Console.WriteLine(result(4));

// Prints True.

// You can also use simplified syntax
// to compile and run an expression tree.
// The following line can replace two previous statements.
Console.WriteLine(expr.Compile()(4));

// Also prints True.

詳細については、「方法: 式ツリーを実行する (C# および Visual Basic)」を参照してください。

参照

処理手順

方法: 式ツリーを実行する (C# および Visual Basic)

方法: 式ツリーを変更する (C# および Visual Basic)

参照

ラムダ式 (C# プログラミング ガイド)

System.Linq.Expressions

概念

動的言語ランタイムの概要

ラムダ式 (Visual Basic)

プログラミングの概念

その他の技術情報

Expression Tree Basics (式ツリーの基礎)

Generating Dynamic Methods with Expression Trees in Visual Studio 2010 (Visual Studio 2010 での式ツリーによる動的メソッドの生成)