Ausdrucksbaumstrukturen mit ErläuterungExpression Trees Explained

Vorheriges – 2 ÜbersichtPrevious -- Overview

Eine Ausdrucksbaumstruktur ist eine Datenstruktur, die Code darstellt.An Expression Tree is a data structure that defines code. Sie basiert auf den gleichen Strukturen, die ein Compiler verwendet, um Code zu analysieren und die kompilierte Ausgabe zu generieren.They are based on the same structures that a compiler uses to analyze code and generate the compiled output. Wenn Sie dieses Tutorial lesen, werden Sie feststellen, dass eine Ähnlichkeit zwischen Ausdrucksbaumstrukturen und den Typen in den Roslyn-APIs vorhanden ist, um Analyzers and CodeFixes (Analyzer und CodeFixes) zu erstellen.As you read through this tutorial, you will notice quite a bit of similarity between Expression Trees and the types used in the Roslyn APIs to build Analyzers and CodeFixes. (Analyzer und CodeFixes sind NuGet-Pakete, die statische Analysen für Code ausführen und mögliche Korrekturen für einen Entwickler vorschlagen können.) Die Konzepte sind ähnlich, und das Endergebnis ist eine Datenstruktur, die eine Prüfung des Quellcodes auf sinnvolle Weise ermöglicht.(Analyzers and CodeFixes are NuGet packages that perform static analysis on code and can suggest potential fixes for a developer.) The concepts are similar, and the end result is a data structure that allows examination of the source code in a meaningful way. Ausdrucksbaumstrukturen basieren jedoch auf einem völlig anderen Satz von Klassen und APIs als die Roslyn-APIs.However, Expression Trees are based on a totally different set of classes and APIs than the Roslyn APIs.

Sehen wir uns ein einfaches Beispiel an.Let's look at a simple example. Hier ist eine Codezeile:Here's a line of code:

var sum = 1 + 2;

Würden Sie dies als eine Ausdrucksbaumstruktur analysieren, enthält die Struktur mehrere Knoten.If you were to analyze this as an expression tree, the tree contains several nodes. Der äußerste Knoten ist eine Variablendeklaration-Anweisung mit der Zuordnung (var sum = 1 + 2;). Dieser äußerste Knoten enthält mehrere untergeordnete Knoten: eine Variablendeklaration, ein Zuweisungsoperator und ein Ausdruck, der die rechte Seite des Gleichheitszeichens darstellt.The outermost node is a variable declaration statement with assignment (var sum = 1 + 2;) That outermost node contains several child nodes: a variable declaration, an assignment operator, and an expression representing the right hand side of the equals sign. Dieser Ausdruck wird weiter unterteilt in Ausdrücke, die den Additionsvorgang und linken und rechten Operanden der Addition darstellen.That expression is further subdivided into expressions that represent the addition operation, and left and right operands of the addition.

Lassen Sie uns die Ausdrücke etwas genauer ansehen, die die rechte Seite neben dem Gleichheitszeichen bilden.Let's drill down a bit more into the expressions that make up the right side of the equals sign. Der Ausdruck ist 1 + 2.The expression is 1 + 2. Das ist ein binärer Ausdruck.That's a binary expression. Genauer gesagt ist es ein binärer Additionsausdruck.More specifically, it's a binary addition expression. Ein binäre Additionsausdruck verfügt über zwei untergeordnete Elemente, die den linken und rechten Knoten des Additionsausdrucks darstellen.A binary addition expression has two children, representing the left and right nodes of the addition expression. Hier handelt es sich bei beiden Knoten um konstante Ausdrücke: Der linke Operand ist der Wert 1, und der rechte Operand ist der Wert 2.Here, both nodes are constant expressions: The left operand is the value 1, and the right operand is the value 2.

Visuell ist die gesamte Anweisung eine Struktur: Sie können beim Stammknoten beginnen und zu jedem Knoten in der Struktur navigieren, um den Code anzuzeigen, der die Anweisung bildet:Visually, the entire statement is a tree: You could start at the root node, and travel to each node in the tree to see the code that makes up the statement:

  • Variablendeklaration-Anweisung mit der Zuordnung (var sum = 1 + 2;)Variable declaration statement with assignment (var sum = 1 + 2;)
    • Implizite Typdeklaration von Variablen (var sum)Implicit variable type declaration (var sum)
      • Implizites var-Schlüsselwort (var)Implicit var keyword (var)
      • Namensdeklaration von Variablen (sum)Variable name declaration (sum)
    • Zuweisungsoperator (=)Assignment operator (=)
    • Binärer Additionsausdruck (1 + 2)Binary addition expression (1 + 2)
      • Linker Operand (1)Left operand (1)
      • Additionsoperator (+)Addition operator (+)
      • Rechter Operand (2)Right operand (2)

Dies mag kompliziert aussehen, aber es ist sehr leistungsfähig.This may look complicated, but it is very powerful. Mit den gleichen Verfahren können Sie wesentlich kompliziertere Ausdrücke zerlegen.Following the same process, you can decompose much more complicated expressions. Betrachten Sie diesen Ausdruck:Consider this expression:

var finalAnswer = this.SecretSauceFunction(
    currentState.createInterimResult(), currentState.createSecondValue(1, 2),
    decisionServer.considerFinalOptions("hello")) +
    MoreSecretSauce('A', DateTime.Now, true);

Der obige Ausdruck ist auch die Variablendeklaration mit einer Zuordnung.The expression above is also a variable declaration with an assignment. In dieser Instanz ist die rechte Seite der Zuordnung eine wesentlich kompliziertere Struktur.In this instance, the right hand side of the assignment is a much more complicated tree. Ich werde diesen Ausdruck nicht zerlegen, aber beachten Sie, was die verschiedenen Knoten sein könnten.I'm not going to decompose this expression, but consider what the different nodes might be. Es sind Methodenaufrufe vorhanden, die das aktuelle Objekt als Empfänger verwenden, einen, der einen expliziten this-Empfänger hat und einen, der das nicht hat.There are method calls using the current object as a receiver, one that has an explicit this receiver, one that does not. Es sind Methodenaufrufe vorhanden, die andere Empfängerobjekte verwenden. Es sind konstante Argumente verschiedener Typen vorhanden.There are method calls using other receiver objects, there are constant arguments of different types. Und schließlich gibt es einen binären Additionsoperator.And finally, there is a binary addition operator. Je nach Rückgabetyp von SecretSauceFunction() oder MoreSecretSauce(), kann dieser binäre Additionsoperator ein Methodenaufruf an einen überschriebenen Additionsoperator sein, der einen statischen Methodenaufruf des binären Additionsoperators, der für eine Klasse definiert ist auflöst.Depending on the return type of SecretSauceFunction() or MoreSecretSauce(), that binary addition operator may be a method call to an overridden addition operator, resolving to a static method call to the binary addition operator defined for a class.

Trotz dieser spürbaren Komplexität erstellt der obige Ausdruck eine Baumstruktur, die so einfach wie das erste Beispiel navigiert werden kann.Despite this perceived complexity, the expression above creates a tree structure that can be navigated as easily as the first sample. Sie können untergeordnete Knoten durchlaufen lassen, um Endknoten im Ausdruck zu finden.You can keep traversing child nodes to find leaf nodes in the expression. Übergeordnete Knoten verfügen über Verweise auf ihre untergeordneten Elemente, und jeder Knoten verfügt über eine Eigenschaft, die beschreibt, welche Art von Knoten es ist.Parent nodes will have references to their children, and each node has a property that describes what kind of node it is.

Die Struktur einer Ausdrucksbaumstruktur ist sehr konsistent.The structure of an expression tree is very consistent. Sobald Sie mit den Grundlagen vertraut sind, können Sie auch den komplexesten Code verstehen, wenn er als Ausdrucksbaumstruktur dargestellt wird.Once you've learned the basics, you can understand even the most complex code when it is represented as an expression tree. Die Eleganz in der Datenstruktur erläutert, wie der C#-Compiler die komplexesten C#-Programme analysieren und eine korrekte Ausgabe aus diesem komplizierten Quellcode erstellen kann.The elegance in the data structure explains how the C# compiler can analyze the most complex C# programs and create proper output from that complicated source code.

Sobald Sie mit der Struktur von Ausdrucksbaumstrukturen vertraut sind, werden Sie feststellen, dass dieses Wissen es Ihnen ermöglicht, mit mehr und mehr erweiterten Szenarios zu arbeiten.Once you become familiar with the structure of expression trees, you will find that knowledge you've gained quickly enables you to work with many more and more advanced scenarios. Dies bedeutet eine unglaubliche Leistung für Ausdrucksbaumstrukturen.There is incredible power to expression trees.

Zusätzlich zum Übersetzen von Algorithmen, die in anderen Umgebungen ausgeführt werden sollen, können Ausdrucksbaumstrukturen verwendet werden, um das Schreiben von Algorithmen zu erleichtern, der Code überprüft, bevor Sie ihn ausführen.In addition to translating algorithms to execute in other environments, expression trees can be used to make it easier to write algorithms that inspect code before executing it. Sie können eine Methode schreiben, deren Argumente Ausdrücke sind, und anschließend diese Ausdrücke vor dem Ausführen des Codes überprüfen.You can write a method whose arguments are expressions and then examine those expressions before executing the code. Die Ausdrucksbaumstruktur ist eine vollständige Darstellung des Codes: Sie können Werte von beliebigen Unterausdrücken sehen.The Expression Tree is a full representation of the code: you can see values of any sub-expression. Sie können Methoden- und Eigenschaftsnamen sehen.You can see method and property names. Sie können den Wert jedes konstanten Ausdrucks sehen.You can see the value of any constant expressions. Sie können auch eine Ausdrucksbaumstruktur in einen ausführbaren Delegaten konvertieren und den Code ausführen.You can also convert an expression tree into an executable delegate, and execute the code.

Mit den APIs für Ausdrucksbaumstrukturen können Sie Strukturen erstellen, die fast jeden gültigen Codekonstrukt darstellen.The APIs for Expression Trees enable you to create trees that represent almost any valid code construct. Um die Dinge so einfach wie möglich zu halten, können jedoch einige C#-Ausdrücke nicht in einer Ausdrucksbaumstruktur erstellt werden.However, to keep things as simple as possible, some C# idioms cannot be created in an expression tree. Ein Beispiel sind asynchrone Ausdrücke (mithilfe der async- und await-Schlüsselwörter).One example is asynchronous expressions (using the async and await keywords). Wenn Ihre Bedürfnisse asynchrone Algorithmen erfordern, müssten Sie die Task-Objekte direkt bearbeiten, anstatt sich auf die Unterstützung des Compilers zu verlassen.If your needs require asynchronous algorithms, you would need to manipulate the Task objects directly, rather than rely on the compiler support. Ein weiteres Beispiel ist die Erstellung von Schleifen.Another is in creating loops. Normalerweise erstellen Sie diese mithilfe der Schleifen for, foreach, while oder do.Typically, you create these by using for, foreach, while or do loops. Wie Sie später in dieser Serie sehen werden, unterstützen die APIs für Ausdrucksbaumstrukturen einen einzelnen Schleifenausdruck mit break- und continue-Ausdrücken, die die Wiederholung der Schleife steuern.As you'll see later in this series, the APIs for expression trees support a single loop expression, with break and continue expressions that control repeating the loop.

Eine Sache, die für Sie nicht möglich ist, ist die Änderung einer Ausdrucksbaumstruktur.The one thing you can't do is modify an expression tree. Ausdrucksbaumstrukturen sind unveränderliche Datenstrukturen.Expression Trees are immutable data structures. Wenn Sie eine Ausdrucksbaumstruktur ändern möchten, müssen Sie eine neue Struktur erstellen, die eine Kopie des Originals ist, aber mit den gewünschten Änderungen.If you want to mutate (change) an expression tree, you must create a new tree that is a copy of the original, but with your desired changes.

Weiter – Framework-Typen, die Ausdrucksbaumstrukturen unterstützenNext -- Framework Types Supporting Expression Trees