Erstellen von AusdrucksbaumstrukturenBuilding Expression Trees

Vorheriges - Interpretieren von AusdrückenPrevious -- Interpreting Expressions

Alle Ausdrucksbaumstrukturen, die Sie bisher gesehen haben, wurden vom C#-Compiler erstellt.All the expression trees you've seen so far have been created by the C# compiler. Sie müssen einfach einen Lambdaausdruck erstellen, der einer typisierten Variable als Expression<Func<T>> oder als einen ähnlichen Typ zugewiesen wurde.All you had to do was create a lambda expression that was assigned to a variable typed as an Expression<Func<T>> or some similar type. Das ist nicht die einzige Möglichkeit, eine Ausdrucksbaumstruktur zu erstellen.That's not the only way to create an expression tree. Für viele Szenarios stellen Sie möglicherweise fest, dass Sie einen Ausdruck im Arbeitsspeicher zur Laufzeit erstellen müssen.For many scenarios you may find that you need to build an expression in memory at runtime.

Das Erstellen von Ausdrucksbaumstrukturen ist kompliziert, da diese Ausdrucksbaumstrukturen unveränderlich sind.Building Expression Trees is complicated by the fact that those expression trees are immutable. Unveränderlich bedeutet, dass Sie die Struktur von den Blättern bis zum Stamm erstellen müssen.Being immutable means that you must build the tree from the leaves up to the root. Die APIs, die Sie zum Erstellen von Ausdrucksbaumstrukturen verwenden spiegeln diese Tatsache wider: Die Methoden, die Sie verwenden, um einen Knoten zu erstellen, werden alle ihre untergeordneten Elemente als Argumente verwenden.The APIs you'll use to build expression trees reflect this fact: The methods you'll use to build a node take all its children as arguments. Betrachten wir einige Beispiele, die Ihnen die Techniken zeigen.Let's walk through a few examples to show you the techniques.

Erstellen von KnotenCreating Nodes

Beginnen wir erneut relativ einfach.Let's start relatively simply again. Wir verwenden den Additionsausdruck, mit dem ich in diesen Abschnitten arbeiten werde:We'll use the addition expression I've been working with throughout these sections:

Expression<Func<int>> sum = () => 1 + 2;

Um diese Ausdrucksbaumstruktur zu erstellen, müssen Sie die Endknoten erstellen.To construct that expression tree, you must construct the leaf nodes. Die Endknoten sind Konstanten, damit Sie die Expression.Constant-Methode verwenden können, um die Knoten zu erstellen:The leaf nodes are constants, so you can use the Expression.Constant method to create the nodes:

var one = Expression.Constant(1, typeof(int));
var two = Expression.Constant(2, typeof(int));

Als Nächstes erstellen Sie den Additionsausdruck:Next, you'll build the addition expression:

var addition = Expression.Add(one, two);

Sobald Sie den Additionsausdruck haben, können Sie den Lambdaausdruck erstellen:Once you've got the addition expression, you can create the lambda expression:

var lamdba = Expression.Lambda(addition);

Dies ist eine sehr einfache LambdaExpression, da sie keine Argumente enthält.This is a very simple LambdaExpression, because it contains no arguments. In diesem Abschnitt erfahren Sie später, wie Sie Parametern Argumente zuordnen und kompliziertere Ausdrücke erstellen.Later in this section, you'll see how to map arguments to parameters and build more complicated expressions.

Für Ausdrücke, die so einfach wie dieser sind, können Sie alle Aufrufe in einer einzelnen Anweisung kombinieren:For expressions that are as simple as this one, you may combine all the calls into a single statement:

var lambda = Expression.Lambda(
    Expression.Add(
        Expression.Constant(1, typeof(int)),
        Expression.Constant(2, typeof(int))
    )
);

Erstellen einer StrukturBuilding a Tree

Das sind die Grundlagen der Erstellung einer Ausdrucksbaumstruktur im Arbeitsspeicher.That's the basics of building an expression tree in memory. Komplexere Strukturen bedeuten im Allgemeinen mehr Knotentypen und weitere Knoten in der Struktur.More complex trees generally mean more node types, and more nodes in the tree. Lassen Sie uns ein weiteres Beispiel ausführen, und zwei weitere Knotentypen, die Sie in der Regel beim Erstellen von Ausdrucksbaumstrukturen erstellen, anzeigen: Die Argumentknoten und Methodenaufrufknoten.Let's run through one more example and show two more node types that you will typically build when you create expression trees: the argument nodes, and method call nodes.

Wir erstellen eine Ausdrucksbaumstruktur, um diesen Ausdruck zu erstellen:Let's build an expression tree to create this expression:

Expression<Func<double, double, double>> distanceCalc =
    (x, y) => Math.Sqrt(x * x + y * y);

Sie beginnen mit dem Erstellen der Parameterausdrücke für x und y:You'll start by creating parameter expressions for x and y:

var xParameter = Expression.Parameter(typeof(double), "x");
var yParameter = Expression.Parameter(typeof(double), "y");

Das Erstellen der Multiplikations- und Additionsausdrücke folgt dem Muster, das Sie bereits gesehen haben:Creating the multiplication and addition expressions follows the pattern you've already seen:

var xSquared = Expression.Multiply(xParameter, xParameter);
var ySquared = Expression.Multiply(yParameter, yParameter);
var sum = Expression.Add(xSquared, ySquared);

Anschließend müssen Sie einen Ausdruck des Methodenaufrufs für den Aufruf von Math.Sqrt erstellen.Next, you need to create a method call expression for the call to Math.Sqrt.

var sqrtMethod = typeof(Math).GetMethod("Sqrt", new[] { typeof(double) });
var distance = Expression.Call(sqrtMethod, sum);

Und anschließend legen Sie den Methodenaufruf in einen Lambdaausdruck, und stellen sicher, dass Sie die Argumente für den Lambdaausdruck definieren:And then finally, you put the method call into a lambda expression, and make sure to define the arguments to the lambda expression:

var distanceLambda = Expression.Lambda(
    distance,
    xParameter,
    yParameter);

In diesem komplizierteren Beispiel sehen Sie ein paar weitere Verfahren, die Sie häufig benötigen, um Ausdrucksbaumstrukturen zu erstellen.In this more complicated example, you see a couple more techniques that you will often need to create expression trees.

Zunächst müssen Sie die Objekte erstellen, die Parameter oder lokale Variablen darstellen, bevor Sie sie verwenden.First, you need to create the objects that represent parameters or local variables before you use them. Wenn Sie diese Objekte erstellt haben, können Sie diese in Ihrer Ausdrucksbaumstruktur verwenden, wo immer Sie sie benötigen.Once you've created those objects, you can use them in your expression tree wherever you need.

Zweitens müssen Sie einen Teil der Reflektions-APIs verwenden, um ein MethodInfo-Objekt zu erstellen, sodass Sie eine Ausdrucksbaumstruktur für den Zugriff auf diese Methode erstellen können.Second, you need to use a subset of the Reflection APIs to create a MethodInfo object so that you can create an expression tree to access that method. Sie müssen sich auf die Teilmenge der Reflektions-APIs begrenzen, die auf der .NET Core-Plattform verfügbar sind.You must limit yourself to the subset of the Reflection APIs that are available on the .NET Core platform. In diesem Fall werden diese Techniken auf andere Ausdrucksbaumstrukturen erweitert.Again, these techniques will extend to other expression trees.

Erstellen von Code im DetailBuilding Code In Depth

Sie sind nicht darin beschränkt, was Sie mithilfe dieser APIs erstellen können.You aren't limited in what you can build using these APIs. Je komplizierter jedoch die Ausdrucksbaumstruktur ist, die Sie erstellen möchten, desto schwieriger ist der Code zu verwalten und zu lesen.However, the more complicated expression tree that you want to build, the more difficult the code is to manage and to read.

Wir erstellen eine Ausdrucksbaumstruktur, die diesem Code entspricht:Let's build an expression tree that is the equivalent of this code:

Func<int, int> factorialFunc = (n) =>
{
    var res = 1;
    while (n > 1)
    {
        res = res * n;
        n--;
    }
    return res;
};

Beachten Sie oben, dass ich nicht die Ausdrucksbaumstruktur, aber einfach den Delegaten erstellt habe.Notice above that I did not build the expression tree, but simply the delegate. Mithilfe der Expression-Klasse können Sie keine Anweisungslambdas erstellen.Using the Expression class, you can't build statement lambdas. Hier ist der Code, der erforderlich ist, um die gleiche Funktionalität zu erstellen.Here's the code that is required to build the same functionality. Es ist etwas kompliziert, dass es keine API zum Erstellen einer while-Schleife gibt. Stattdessen müssen Sie eine Schleife, die einen bedingten Test enthält, und ein Bezeichnungsziel erstellen, um die Schleife zu unterbrechen.It's complicated by the fact that there isn't an API to build a while loop, instead you need to build a loop that contains a conditional test, and a label target to break out of the loop.

var nArgument = Expression.Parameter(typeof(int), "n");
var result = Expression.Variable(typeof(int), "result");

// Creating a label that represents the return value
LabelTarget label = Expression.Label(typeof(int));

var initializeResult = Expression.Assign(result, Expression.Constant(1));

// This is the inner block that performs the multiplication,
// and decrements the value of 'n'
var block = Expression.Block(
    Expression.Assign(result,
        Expression.Multiply(result, nArgument)),
    Expression.PostDecrementAssign(nArgument)
);

// Creating a method body.
BlockExpression body = Expression.Block(
    new[] { result },
    initializeResult,
    Expression.Loop(
        Expression.IfThenElse(
            Expression.GreaterThan(nArgument, Expression.Constant(1)),
            block,
            Expression.Break(label, result)
        ),
        label
    )
);

Der Code zum Erstellen der Baumstruktur für die Fakultätsfunktion ist etwas länger, komplizierter, und er ist voll von Bezeichnungen und Break-Anweisungen und anderen Elemente, die wir in unseren täglichen Codieraufgaben vermeiden möchten.The code to build the expression tree for the factorial function is quite a bit longer, more complicated, and it's riddled with labels and break statements and other elements we'd like to avoid in our everyday coding tasks.

Für diesen Abschnitt habe ich auch den Besuchercode aktualisiert, um jeden Knoten in dieser Ausdrucksbaumstruktur zu finden, und Informationen zu den Knoten, die in diesem Beispiel erstellt wurden, zu schreiben.For this section, I've also updated the visitor code to visit every node in this expression tree and write out information about the nodes that are created in this sample. Sie können den Beispielcode vom Repository „dotnet/docs“ auf GitHub anzeigen oder herunterladen.You can view or download the sample code at the dotnet/docs GitHub repository. Experimentieren Sie selbst, indem Sie die Beispiele erstellen und ausführen.Experiment for yourself by building and running the samples. Anweisungen zum Herunterladen finden Sie unter Beispiele und Lernprogramme.For download instructions, see Samples and Tutorials.

Untersuchen der APIsExamining the APIs

Die Ausdrucksbaumstruktur-APIs sind einige der Schwierigeren zum Navigieren in .NET Core, aber das ist in Ordnung.The expression tree APIs are some of the more difficult to navigate in .NET Core, but that's fine. Ihr Zweck ist ein recht komplexes Unterfangen: Schreiben von Code, der Code zur Laufzeit generiert.Their purpose is a rather complex undertaking: writing code that generates code at runtime. Sie sind notwendigerweise kompliziert, um ein Gleichgewicht zwischen der Unterstützung aller verfügbaren Steuerungsstrukturen in der C#-Sprache bereitzustellen und um die Oberfläche der APIs so klein wie angemessenen zu halten.They are necessarily complicated to provide a balance between supporting all the control structures available in the C# language and keeping the surface area of the APIs as small as reasonable. Diese Balance bedeutet, dass viele Steuerungsstrukturen nicht über die C#-Konstrukte dargestellt werden, jedoch über Konstrukte, die die zugrunde liegende Logik darstellen, die der Compiler von diesen Konstrukten mit höherer Ebene generiert.This balance means that many control structures are represented not by their C# constructs, but by constructs that represent the underlying logic that the compiler generates from these higher level constructs.

Außerdem sind zu diesem Zeitpunkt C#-Ausdrücke vorhanden, die nicht direkt mit Expression-Klassenmethoden erstellt werden können.Also, at this time, there are C# expressions that cannot be built directly using Expression class methods. Im Allgemeinen werden dies die neuesten Operatoren und Ausdrücke sein, die in C# 5 und C# 6 hinzugefügt werden.In general, these will be the newest operators and expressions added in C# 5 and C# 6. (Z.B. async-Ausdrücke können nicht erstellt werden, und der neue ?.-Operator kann nicht direkt erstellt werden.)(For example, async expressions cannot be built, and the new ?. operator cannot be directly created.)

Weiter– Übersetzen von AusdrückenNext -- Translating Expressions