식 트리(C# 및 Visual Basic)

식 트리는 코드를 트리 형태의 데이터 구조로 표현한 것입니다. 이 트리의 각 노드는 식을 나타냅니다. 예를 들어 각 노드는 x < y 같은 이항 연산 또는 메서드 호출일 수 있습니다.

식 트리로 표현된 코드를 컴파일하고 실행할 수 있습니다. 이렇게 하면 실행 코드를 동적으로 수정하고, 여러 데이터베이스에서 LINQ 쿼리를 실행하고, 동적 쿼리를 만들 수 있습니다. LINQ의 식 트리에 대한 자세한 내용은 방법: 식 트리를 사용하여 동적 쿼리 빌드(C# 및 Visual Basic)연습: IQueryable LINQ 공급자 만들기를 참조하십시오.

식 트리는 동적 언어와 .NET Framework 사이에 상호 운용성을 제공하고 컴파일러 작성기가 MSIL(Microsoft Intermediate Language) 대신 식 트리를 내보낼 수 있도록 하기 위해 DLR(동적 언어 런타임)에도 사용됩니다. DLR에 대한 자세한 내용은 동적 언어 런타임 개요를 참조하십시오.

C# 또는 Visual Basic 컴파일러를 사용하여 익명 람다 식을 기반으로 한 식 트리를 자동으로 만들 수도 있고 System.Linq.Expressions 네임스페이스를 사용하여 식 트리를 수동으로 만들 수도 있습니다.

람다 식으로 식 트리 만들기

람다 식이 Expression<TDelegate> 형식의 변수에 할당된 경우 컴파일러에서 람다 식을 나타내는 식 트리를 빌드할 코드를 내보냅니다.

C# 및 Visual Basic 컴파일러는 식 람다(또는 한 줄로 된 람다)를 통해서만 식 트리를 생성할 수 있습니다. 문 람다(또는 여러 줄로 된 람다)는 컴파일러에서 구문 분석할 수 없습니다. C#의 람다 식에 대한 자세한 내용은 람다 식(C# 프로그래밍 가이드)을 참조하고, Visual Basic의 람다 식에 대해서는 람다 식(Visual Basic)을 참조하십시오.

다음 코드 예제에서는 C# 및 Visual Basic 컴파일러를 통해 람다 식 num => num < 5(C#) 또는 Function(num) num < 5(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은 메서드 호출을 나타냅니다. ParameterExpression, MethodCallExpression 및 기타 식 관련 형식도 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을 참조하십시오.

식 트리 구문 분석

다음 코드 예제에서는 람다 식 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

프로그래밍 개념