Stromy výrazů (C#)

Stromy výrazů reprezentují kód v datové struktuře podobné stromu, kde každý uzel je výraz, například volání metody nebo binární operace, jako je například x < y .

Můžete zkompilovat a spustit kód reprezentovaný stromy výrazů. To umožňuje dynamickou úpravu spustitelného kódu, spuštění dotazů LINQ v různých databázích a vytváření dynamických dotazů. Další informace o stromech výrazů v LINQ naleznete v tématu How to use Expression Trees to Dynamic dotazy (C#).

Stromy výrazů se také používají v dynamickém jazykovém modulu runtime (DLR) k zajištění interoperability mezi dynamickými jazyky a rozhraním .NET a umožňují zapisovačům kompilátoru vysílat stromy výrazů namísto jazyka MSIL (Microsoft Intermediate Language). Další informace o DLR najdete v tématu Přehled dynamického jazykového modulu runtime.

můžete mít kompilátor C# nebo Visual Basic vytvořit strom výrazu pro vás na základě anonymního výrazu lambda, nebo můžete vytvořit stromy výrazů ručně pomocí System.Linq.Expressions oboru názvů.

Vytváření stromů výrazů ze výrazů lambda

Když je výraz lambda přiřazen proměnné typu Expression<TDelegate> , kompilátor generuje kód pro sestavení stromu výrazu, který reprezentuje lambda výraz.

Kompilátor jazyka C# může generovat stromy výrazů pouze z výrazů lambda výrazů (nebo jednoduchých výrazů lambda). Nelze analyzovat výrazy lambda příkazů (nebo víceřádkové výrazy lambda). Další informace o výrazech lambda v jazyce C# naleznete v tématu lambda Expressions.

Následující příklady kódu ukazují, jak má kompilátor jazyka C# vytvořit strom výrazu, který představuje výraz lambda num => num < 5 .

Expression<Func<int, bool>> lambda = num => num < 5;  

Vytváření stromů výrazů pomocí rozhraní API

Chcete-li vytvořit stromy výrazů pomocí rozhraní API, použijte Expression třídu. Tato třída obsahuje statické metody továrny, které vytvářejí uzly stromu výrazů specifických typů, například ParameterExpression , které představují proměnnou nebo parametr, nebo MethodCallExpression , které představují volání metody. ParameterExpression, MethodCallExpression a další typy specifické pro výraz jsou také definovány v System.Linq.Expressions oboru názvů. Tyto typy jsou odvozeny z abstraktního typu Expression .

Následující příklad kódu ukazuje, jak vytvořit strom výrazu, který reprezentuje lambda výraz num => num < 5 pomocí rozhraní API.

// 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 });  

v .NET Framework 4 nebo novějších rozhraní API stromů výrazů podporuje také přiřazení a vývojové výrazy řízení, jako jsou smyčky, podmíněné bloky a try-catch bloky. Pomocí rozhraní API můžete vytvořit stromy výrazů, které jsou složitější než ty, které lze vytvořit z výrazů lambda kompilátorem jazyka C#. Následující příklad ukazuje, jak vytvořit strom výrazu, který vypočítá faktoriál čísla.

// 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.  

další informace naleznete v tématu generování dynamických metod pomocí stromů výrazů v Visual Studio 2010, které platí také pro novější verze Visual Studio.

Analýza stromů výrazů

Následující příklad kódu ukazuje, jak je možné rozložit strom výrazu reprezentující výraz lambda num => num < 5 na jeho části.

// 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  

Neměnnosti stromů výrazů

Stromy výrazů by měly být neměnné. To znamená, že pokud chcete upravit strom výrazu, je nutné vytvořit nový strom výrazu zkopírováním stávajícího a nahrazením uzlů v něm. K procházení stávajícího stromu výrazů můžete použít návštěvníka stromu výrazu. Další informace najdete v tématu Postup úpravy stromů výrazů (C#).

Kompilování stromů výrazů

Expression<TDelegate>Typ poskytuje Compile metodu, která zkompiluje kód reprezentovaný stromem výrazu do delegáta spustitelného souboru.

Následující příklad kódu ukazuje, jak zkompilovat strom výrazu a spustit výsledný kód.

// 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.  

Další informace najdete v tématu spuštění stromů výrazů (C#).

Viz také