Interpretieren von AusdrückenInterpreting Expressions

Vorheriges – Ausführen von AusdrückenPrevious -- Executing Expressions

Lassen Sie uns nun Code schreiben, um die Struktur einer Ausdrucksbaumstruktur zu untersuchen.Now, let's write some code to examine the structure of an expression tree. Jeder Knoten in einer Ausdrucksbaumstruktur ist ein Objekt einer Klasse, die von Expression abgeleitet ist.Every node in an expression tree will be an object of a class that is derived from Expression.

Dieser Entwurf macht den Zugriff auf alle Knoten in einer Ausdrucksbaumstruktur zu einem relativ unkomplizierten rekursiven Vorgang.That design makes visiting all the nodes in an expression tree a relatively straight forward recursive operation. Die allgemeine Strategie besteht darin, im Stammknoten zu starten und zu bestimmen, welche Art von Knoten es ist.The general strategy is to start at the root node and determine what kind of node it is.

Wenn der Knotentyp untergeordnete Elemente besitzt, greifen Sie rekursiv auf die untergeordneten Elemente zu.If the node type has children, recursively visit the children. Wiederholen Sie den beim Stammknoten verwendeten Prozess bei jedem untergeordneten Knoten: Bestimmen Sie den Typ, und wenn er untergeordnete Elemente aufweist, greifen Sie auf jedes der untergeordneten Elemente zu.At each child node, repeat the process used at the root node: determine the type, and if the type has children, visit each of the children.

Untersuchen eines Ausdrucks ohne untergeordnete ElementeExamining an Expression with No Children

Beginnen wir damit, auf jeden Knoten in einer einfachen Ausdrucksbaumstruktur zuzugreifen.Let's start by visiting each node in a very simple expression tree. Hier ist der Code, der einen konstanten Ausdruck erstellt und anschließend seine Eigenschaften überprüft:Here's the code that creates a constant expression and then examines its properties:

var constant = Expression.Constant(24, typeof(int));

Console.WriteLine($"This is a/an {constant.NodeType} expression type");
Console.WriteLine($"The type of the constant value is {constant.Type}");
Console.WriteLine($"The value of the constant value is {constant.Value}");

Dadurch wird Folgendes zurückgegeben:This will print the following:

This is an Constant expression type
The type of the constant value is System.Int32
The value of the constant value is 24

Jetzt schreiben wir den Code, der diesen Ausdruck untersuchen und einige wichtige Eigenschaften darüber schreiben würde.Now, let's write the code that would examine this expression and write out some important properties about it. Hier ist dieser Code:Here's that code:

Untersuchen eines einfachen AdditionsausdrucksExamining a simple Addition Expression

Beginnen wir mit dem Hinzufügen-Beispiel aus der Einführung zu diesem Abschnitt.Let's start with the addition sample from the introduction to this section.

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

Ich verwende kein var, um diese Ausdrucksbaumstruktur zu deklarieren, da es nicht möglich ist, weil die rechte Seite der Zuweisung implizit typisiert ist.I'm not using var to declare this expression tree, as it is not possible because the right-hand side of the assignment is implicitly typed. Um dies besser zu verstehen, lesen Sie hier.To understand this more deeply, read here.

Der Stammknoten ist ein LambdaExpression.The root node is a LambdaExpression. Um interessanten Code auf der rechten Seite des =>-Operators zu erhalten, müssen Sie eines der untergeordneten Elemente des LambdaExpression finden.In order to get the interesting code on the right hand side of the => operator, you need to find one of the children of the LambdaExpression. Wir werden dies mit allen Ausdrücken in diesem Abschnitt durchführen.We'll do that with all the expressions in this section. Der übergeordnete Knoten hilft uns beim Finden des Rückgabetyps des LambdaExpression.The parent node does help us find the return type of the LambdaExpression.

Wir müssen rekursiv auf eine Anzahl von Knoten zugreifen, um jeden Knoten in diesem Ausdruck zu untersuchen.To examine each node in this expression, we'll need to recursively visit a number of nodes. Hier ist eine einfache erste Implementierung:Here's a simple first implementation:

Expression<Func<int, int, int>> addition = (a, b) => a + b;

Console.WriteLine($"This expression is a {addition.NodeType} expression type");
Console.WriteLine($"The name of the lambda is {((addition.Name == null) ? "<null>" : addition.Name)}");
Console.WriteLine($"The return type is {addition.ReturnType.ToString()}");
Console.WriteLine($"The expression has {addition.Parameters.Count} arguments. They are:");
foreach(var argumentExpression in addition.Parameters)
{
    Console.WriteLine($"\tParameter Type: {argumentExpression.Type.ToString()}, Name: {argumentExpression.Name}");
}

var additionBody = (BinaryExpression)addition.Body;
Console.WriteLine($"The body is a {additionBody.NodeType} expression");
Console.WriteLine($"The left side is a {additionBody.Left.NodeType} expression");
var left = (ParameterExpression)additionBody.Left;
Console.WriteLine($"\tParameter Type: {left.Type.ToString()}, Name: {left.Name}");
Console.WriteLine($"The right side is a {additionBody.Right.NodeType} expression");
var right= (ParameterExpression)additionBody.Right;
Console.WriteLine($"\tParameter Type: {right.Type.ToString()}, Name: {right.Name}");

Dieses Beispiel gibt die folgende Ausgabe zurück:This sample prints the following output:

This expression is a/an Lambda expression type
The name of the lambda is <null>
The return type is System.Int32
The expression has 2 arguments. They are:
        Parameter Type: System.Int32, Name: a
        Parameter Type: System.Int32, Name: b
The body is a/an Add expression
The left side is a Parameter expression
        Parameter Type: System.Int32, Name: a
The right side is a Parameter expression
        Parameter Type: System.Int32, Name: b

Sie werden viele Wiederholungen im obigen Codebeispiel sehen.You'll notice a lot of repetition in the code sample above. Lassen Sie uns dies bereinigen und einen Ausdrucksknoten für Besucher für eine allgemeinere Verwendung erstellen.Let's clean that up and build a more general purpose expression node visitor. Dafür müssen wir einen rekursiven Algorithmus schreiben.That's going to require us to write a recursive algorithm. Jeder Knoten kann ein Typ sein, der möglicherweise untergeordnete Elemente aufweist.Any node could be of a type that might have children. Jeder Knoten mit untergeordneten Elementen erfordert es, dass Sie auf diese untergeordneten Elemente zugreifen und bestimmen, was dieser Knoten ist.Any node that has children requires us to visit those children and determine what that node is. Hier finden Sie die bereinigte Version, die Rekursion verwendet, um auf die Additionsvorgänge zuzugreifen:Here's the cleaned up version that utilizes recursion to visit the addition operations:

// Base Visitor class:
public abstract class Visitor
{
    private readonly Expression node;

    protected Visitor(Expression node)
    {
        this.node = node;
    }

    public abstract void Visit(string prefix);

    public ExpressionType NodeType => this.node.NodeType;
    public static Visitor CreateFromExpression(Expression node)
    {
        switch(node.NodeType)
        {
            case ExpressionType.Constant:
                return new ConstantVisitor((ConstantExpression)node);
            case ExpressionType.Lambda:
                return new LambdaVisitor((LambdaExpression)node);
            case ExpressionType.Parameter:
                return new ParameterVisitor((ParameterExpression)node);
            case ExpressionType.Add:
                return new BinaryVisitor((BinaryExpression)node);
            default:
                Console.Error.WriteLine($"Node not processed yet: {node.NodeType}");
                return default(Visitor);
        }
    }
}

// Lambda Visitor
public class LambdaVisitor : Visitor
{
    private readonly LambdaExpression node;
    public LambdaVisitor(LambdaExpression node) : base(node)
    {
        this.node = node;
    }

    public override void Visit(string prefix)
    {
        Console.WriteLine($"{prefix}This expression is a {NodeType} expression type");
        Console.WriteLine($"{prefix}The name of the lambda is {((node.Name == null) ? "<null>" : node.Name)}");
        Console.WriteLine($"{prefix}The return type is {node.ReturnType.ToString()}");
        Console.WriteLine($"{prefix}The expression has {node.Parameters.Count} argument(s). They are:");
        // Visit each parameter:
        foreach (var argumentExpression in node.Parameters)
        {
            var argumentVisitor = Visitor.CreateFromExpression(argumentExpression);
            argumentVisitor.Visit(prefix + "\t");
        }
        Console.WriteLine($"{prefix}The expression body is:");
        // Visit the body:
        var bodyVisitor = Visitor.CreateFromExpression(node.Body);
        bodyVisitor.Visit(prefix + "\t");
    }
}

// Binary Expression Visitor:
public class BinaryVisitor : Visitor
{
    private readonly BinaryExpression node;
    public BinaryVisitor(BinaryExpression node) : base(node)
    {
        this.node = node;
    }

    public override void Visit(string prefix)
    {
        Console.WriteLine($"{prefix}This binary expression is a {NodeType} expression");
        var left = Visitor.CreateFromExpression(node.Left);
        Console.WriteLine($"{prefix}The Left argument is:");
        left.Visit(prefix + "\t");
        var right = Visitor.CreateFromExpression(node.Right);
        Console.WriteLine($"{prefix}The Right argument is:");
        right.Visit(prefix + "\t");
    }
}

// Parameter visitor:
public class ParameterVisitor : Visitor
{
    private readonly ParameterExpression node;
    public ParameterVisitor(ParameterExpression node) : base(node)
    {
        this.node = node;
    }

    public override void Visit(string prefix)
    {
        Console.WriteLine($"{prefix}This is an {NodeType} expression type");
        Console.WriteLine($"{prefix}Type: {node.Type.ToString()}, Name: {node.Name}, ByRef: {node.IsByRef}");
    }
}

// Constant visitor:
public class ConstantVisitor : Visitor
{
    private readonly ConstantExpression node;
    public ConstantVisitor(ConstantExpression node) : base(node)
    {
        this.node = node;
    }

    public override void Visit(string prefix)
    {
        Console.WriteLine($"{prefix}This is an {NodeType} expression type");
        Console.WriteLine($"{prefix}The type of the constant value is {node.Type}");
        Console.WriteLine($"{prefix}The value of the constant value is {node.Value}");
    }
}

Dieser Algorithmus ist die Grundlage für einen Algorithmus, der jeden beliebigen LambdaExpression besuchen kann.This algorithm is the basis of an algorithm that can visit any arbitrary LambdaExpression. Es gibt viele Löcher, da der Code, den ich erstellt habe, nur nach einem kleinen Teil der möglichen Sätze von Knoten in der Ausdrucksbaumstruktur sucht, die auftreten können.There are a lot of holes, namely that the code I created only looks for a very small sample of the possible sets of expression tree nodes that it may encounter. Allerdings können Sie dennoch etwas von dem lernen, was er produziert.However, you can still learn quite a bit from what it produces. (Der Standardfall in der Visitor.CreateFromExpression-Methode gibt eine Meldung an die Fehlerkonsole, wenn ein neuer Knotentyp gefunden wird.(The default case in the Visitor.CreateFromExpression method prints a message to the error console when a new node type is encountered. Auf diese Weise wissen Sie, dass Sie einen neuen Ausdruck hinzufügen können.)That way, you know to add a new expression type.)

Beim Ausführen dieser Besucher auf dem oben gezeigten Additionsausdruck erhalten Sie die folgende Ausgabe:When you run this visitor on the addition expression shown above, you get the following output:

This expression is a/an Lambda expression type
The name of the lambda is <null>
The return type is System.Int32
The expression has 2 argument(s). They are:
        This is an Parameter expression type
        Type: System.Int32, Name: a, ByRef: False
        This is an Parameter expression type
        Type: System.Int32, Name: b, ByRef: False
The expression body is:
        This binary expression is a Add expression
        The Left argument is:
                This is an Parameter expression type
                Type: System.Int32, Name: a, ByRef: False
        The Right argument is:
                This is an Parameter expression type
                Type: System.Int32, Name: b, ByRef: False

Nun, da Sie eine allgemeinere Besucherimplementierung erstellt haben, können Sie auf mehr verschiedene Ausdruckstypen zugreifen und diese verarbeiten.Now that you've built a more general visitor implementation, you can visit and process many more different types of expressions.

Untersuchen eines Additionsausdrucks mit vielen EbenenExamining an Addition Expression with Many Levels

Wir versuchen ein etwas komplizierteres Beispiel, aber trotzdem beschränken wir die Knotentypen auf Addition:Let's try a more complicated example, yet still limit the node types to addition only:

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

Bevor Sie dies auf dem Besucheralgorithmus ausführen, versuchen Sie eine Denkübung, um herauszufinden, was die Ausgabe sein könnte.Before you run this on the visitor algorithm, try a thought exercise to work out what the output might be. Beachten Sie, dass der +-Operator ein binärer Operator ist: Er muss über zwei untergeordnete Elemente verfügen, die die linken und rechten Operanden darstellen.Remember that the + operator is a binary operator: it must have two children, representing the left and right operands. Es gibt mehrere Möglichkeiten, eine Struktur zu erstellen, die richtig sein könnte:There are several possible ways to construct a tree that could be correct:

Expression<Func<int>> sum1 = () => 1 + (2 + (3 + 4));
Expression<Func<int>> sum2 = () => ((1 + 2) + 3) + 4;

Expression<Func<int>> sum3 = () => (1 + 2) + (3 + 4);
Expression<Func<int>> sum4 = () => 1 + ((2 + 3) + 4);
Expression<Func<int>> sum5 = () => (1 + (2 + 3)) + 4;

Sie sehen die Aufteilung in zwei mögliche Antworten, um die vielversprechendste zu markieren.You can see the separation into two possible answers to highlight the most promising. Die erste stellt rechtsassoziative Ausdrücke dar.The first represents right associative expressions. Die zweite stellt linksassoziative Ausdrücke dar.The second represent left associative expressions. Der Vorteil dieser beiden Formate ist, dass das Format auf jede beliebige Anzahl von Additionsausdrücken skaliert.The advantage of both of those two formats is that the format scales to any arbitrary number of addition expressions.

Wenn Sie diesen Ausdruck über die Besucher ausführen, sehen Sie diese Ausgabe, die überprüft, ob der einfache Additionsausdruck linksassoziativ ist.If you do run this expression through the visitor, you will see this this output, verifying that the simple addition expression is left associative.

Um dieses Beispiel auszuführen und die vollständige Ausdrucksbaumstruktur anzuzeigen, musste ich eine Änderung an der Quelle der Ausdrucksbaumstruktur vornehmen.In order to run this sample, and see the full expression tree, I had to make one change to the source expression tree. Wenn die Ausdrucksbaumstruktur alle Konstanten enthält, enthält die resultierende Struktur einfach den konstanten Wert von 10.When the expression tree contains all constants, the resulting tree simply contains the constant value of 10. Der Compiler führt alle Additionen aus und reduziert den Ausdruck auf seine einfachste Form.The compiler performs all the addition and reduces the expression to its simplest form. Das Hinzufügen einer Variablen im Ausdruck ist ausreichend, um die ursprüngliche Struktur anzuzeigen:Simply adding one variable in the expression is sufficient to see the original tree:

Expression<Func<int, int>> sum = (a) => 1 + a + 3 + 4;

Erstellen Sie einen Besucher für diese Summe, und führen Sie den Besucher aus, für den Sie diese Ausgabe sehen:Create a visitor for this sum and run the visitor you'll see this output:

This expression is a/an Lambda expression type
The name of the lambda is <null>
The return type is System.Int32
The expression has 1 argument(s). They are:
        This is an Parameter expression type
        Type: System.Int32, Name: a, ByRef: False
The expression body is:
        This binary expression is a Add expression
        The Left argument is:
                This binary expression is a Add expression
                The Left argument is:
                        This binary expression is a Add expression
                        The Left argument is:
                                This is an Constant expression type
                                The type of the constant value is System.Int32
                                The value of the constant value is 1
                        The Right argument is:
                                This is an Parameter expression type
                                Type: System.Int32, Name: a, ByRef: False
                The Right argument is:
                        This is an Constant expression type
                        The type of the constant value is System.Int32
                        The value of the constant value is 3
        The Right argument is:
                This is an Constant expression type
                The type of the constant value is System.Int32
                The value of the constant value is 4

Sie können auch eines der anderen Beispiele über den Besuchercode ausführen und sehen, welche Struktur es darstellt.You can also run any of the other samples through the visitor code and see what tree it represents. Hier ist ein Beispiel für den oben stehenden sum3-Ausdruck (mit einem zusätzlichen Parameter, um zu verhindern, dass der Compiler die Konstante berechnet):Here's an example of the sum3 expression above (with an additional parameter to prevent the compiler from computing the constant):

Expression<Func<int, int, int>> sum3 = (a, b) => (1 + a) + (3 + b);

Dies ist die Ausgabe vom Besucher:Here's the output from the visitor:

This expression is a/an Lambda expression type
The name of the lambda is <null>
The return type is System.Int32
The expression has 2 argument(s). They are:
        This is an Parameter expression type
        Type: System.Int32, Name: a, ByRef: False
        This is an Parameter expression type
        Type: System.Int32, Name: b, ByRef: False
The expression body is:
        This binary expression is a Add expression
        The Left argument is:
                This binary expression is a Add expression
                The Left argument is:
                        This is an Constant expression type
                        The type of the constant value is System.Int32
                        The value of the constant value is 1
                The Right argument is:
                        This is an Parameter expression type
                        Type: System.Int32, Name: a, ByRef: False
        The Right argument is:
                This binary expression is a Add expression
                The Left argument is:
                        This is an Constant expression type
                        The type of the constant value is System.Int32
                        The value of the constant value is 3
                The Right argument is:
                        This is an Parameter expression type
                        Type: System.Int32, Name: b, ByRef: False

Beachten Sie, dass die Klammern nicht Teil der Ausgabe sind.Notice that the parentheses are not part of the output. Es sind keine Knoten in der Ausdrucksbaumstruktur vorhanden, die die Klammern im eingegebenen Ausdruck darstellen.There are no nodes in the expression tree that represent the parentheses in the input expression. Die Struktur der Ausdrucksbaumstruktur enthält alle Informationen, die erforderlich sind, um die Rangfolge zu kommunizieren.The structure of the expression tree contains all the information necessary to communicate the precedence.

Erweiterungen aus diesem BeispielExtending from this sample

Das Beispiel behandelt nur die elementarsten Ausdrucksbaumstrukturen.The sample deals with only the most rudimentary expression trees. Der Code, den Sie in diesem Abschnitt gesehen haben, behandelt nur konstante ganze Zahlen und den binären +-Operator.The code you've seen in this section only handles constant integers and the binary + operator. Als letztes Beispiel aktualisieren wir den Besucher, um einen komplizierteren Ausdruck zu behandeln.As a final sample, let's update the visitor to handle a more complicated expression. Lassen Sie uns dafür sorgen, dass es hierfür funktioniert:Let's make it work for this:

Expression<Func<int, int>> factorial = (n) =>
    n == 0 ? 
    1 : 
    Enumerable.Range(1, n).Aggregate((product, factor) => product * factor);

Dieser Code stellt eine mögliche Implementierung für die mathematische Fakultät-Funktion dar.This code represents one possible implementation for the mathematical factorial function. So wie ich diesen Code geschrieben habe, werden zwei Einschränkungen beim Erstellen von Ausdrucksbaumstrukturen durch die Zuweisung von Lambdaausdrücken an Ausdrücke hervorgehoben.The way I've written this code highlights two limitiations of building expression trees by assigning lambda expressions to Expressions. Erstens sind Anweisungslambdas nicht zulässig.First, statement lambdas are not allowed. Das bedeutet, ich kann keine Schleifen, Blöcke, if/else-Anweisungen und anderen allgemeinen Steuerungsstrukturen in C# verwenden.That means I can't use loops, blocks, if / else statements, and other control structures common in C#. Ich kann nur Ausdrücke verwenden.I'm limited to using expressions. Zweitens kann ich nicht denselben Ausdruck rekursiv aufrufen.Second, I can't recursively call the same expression. Ich könnte dies, wenn er bereits ein Delegat wäre, aber ich kann ihn nicht in seiner Form der Ausdrucksbaumstruktur aufrufen.I could if it were already a delegate, but I can't call it in its expression tree form. Im Abschnitt zu building expression trees (Erstellen von Ausdrucksbaumstrukturen) werden Sie Techniken erlernen, um diese Einschränkung zu umgehen.In the section on building expression trees you'll learn techniques to overcome these limitations.

In diesem Ausdruck können Knoten all dieser Typen auftreten:In this expression, you'll encounter nodes of all these types:

  1. Gleich (binärer Ausdruck)Equal (binary expression)
  2. Multiplizieren (binärer Ausdruck)Multiply (binary expression)
  3. Bedingt (der ?Conditional (the ? : Ausdruck): expression)
  4. Ausdruck des Methodenaufrufs (Aufrufen von Range() und Aggregate())Method Call Expression (calling Range() and Aggregate())

Eine Möglichkeit zum Ändern des Besucheralgorithmus besteht darin, ihn auszuführen, und den Knotentyp jedes Mal, wenn Sie Ihre default-Klausel erreichen, zu schreiben.One way to modify the visitor algorithm is to keep executing it, and write the node type every time you reach your default clause. Nach einigen Iterationen haben Sie alle möglichen Knoten gesehen.After a few iterations, you'll have seen each of the potential nodes. Dann haben Sie alles, was Sie benötigen.Then, you have all you need. Das Ergebnis würde in etwa wie folgt aussehen:The result would be something like this:

public static Visitor CreateFromExpression(Expression node)
{
    switch(node.NodeType)
    {
        case ExpressionType.Constant:
            return new ConstantVisitor((ConstantExpression)node);
        case ExpressionType.Lambda:
            return new LambdaVisitor((LambdaExpression)node);
        case ExpressionType.Parameter:
            return new ParameterVisitor((ParameterExpression)node);
        case ExpressionType.Add:
        case ExpressionType.Equal:
        case ExpressionType.Multiply:
            return new BinaryVisitor((BinaryExpression)node);
        case ExpressionType.Conditional:
            return new ConditionalVisitor((ConditionalExpression)node);
        case ExpressionType.Call:
            return new MethodCallVisitor((MethodCallExpression)node);
        default:
            Console.Error.WriteLine($"Node not processed yet: {node.NodeType}");
            return default(Visitor);
    }
}

ConditionalVisitor und MethodCallVisitor verarbeiten diese zwei Knoten:The ConditionalVisitor and MethodCallVisitor process those two nodes:

public class ConditionalVisitor : Visitor
{
    private readonly ConditionalExpression node;
    public ConditionalVisitor(ConditionalExpression node) : base(node)
    {
        this.node = node;
    }

    public override void Visit(string prefix)
    {
        Console.WriteLine($"{prefix}This expression is a {NodeType} expression");
        var testVisitor = Visitor.CreateFromExpression(node.Test);
        Console.WriteLine($"{prefix}The Test for this expression is:");
        testVisitor.Visit(prefix + "\t");
        var trueVisitor = Visitor.CreateFromExpression(node.IfTrue);
        Console.WriteLine($"{prefix}The True clause for this expression is:");
        trueVisitor.Visit(prefix + "\t");
        var falseVisitor = Visitor.CreateFromExpression(node.IfFalse);
        Console.WriteLine($"{prefix}The False clause for this expression is:");
        falseVisitor.Visit(prefix + "\t");
    }
}

public class MethodCallVisitor : Visitor
{
    private readonly MethodCallExpression node;
    public MethodCallVisitor(MethodCallExpression node) : base(node)
    {
        this.node = node;
    }

    public override void Visit(string prefix)
    {
        Console.WriteLine($"{prefix}This expression is a {NodeType} expression");
        if (node.Object == null)
            Console.WriteLine($"{prefix}This is a static method call");
        else
        {
            Console.WriteLine($"{prefix}The receiver (this) is:");
            var receiverVisitor = Visitor.CreateFromExpression(node.Object);
            receiverVisitor.Visit(prefix + "\t");
        }

        var methodInfo = node.Method;
        Console.WriteLine($"{prefix}The method name is {methodInfo.DeclaringType}.{methodInfo.Name}");
        // There is more here, like generic arguments, and so on.
        Console.WriteLine($"{prefix}The Arguments are:");
        foreach(var arg in node.Arguments)
        {
            var argVisitor = Visitor.CreateFromExpression(arg);
            argVisitor.Visit(prefix + "\t");
        }
    }
}

Und die Ausgabe für die Ausdrucksbaumstruktur wäre:And the output for the expression tree would be:

This expression is a/an Lambda expression type
The name of the lambda is <null>
The return type is System.Int32
The expression has 1 argument(s). They are:
        This is an Parameter expression type
        Type: System.Int32, Name: n, ByRef: False
The expression body is:
        This expression is a Conditional expression
        The Test for this expression is:
                This binary expression is a Equal expression
                The Left argument is:
                        This is an Parameter expression type
                        Type: System.Int32, Name: n, ByRef: False
                The Right argument is:
                        This is an Constant expression type
                        The type of the constant value is System.Int32
                        The value of the constant value is 0
        The True clause for this expression is:
                This is an Constant expression type
                The type of the constant value is System.Int32
                The value of the constant value is 1
        The False clause for this expression is:
                This expression is a Call expression
                This is a static method call
                The method name is System.Linq.Enumerable.Aggregate
                The Arguments are:
                        This expression is a Call expression
                        This is a static method call
                        The method name is System.Linq.Enumerable.Range
                        The Arguments are:
                                This is an Constant expression type
                                The type of the constant value is System.Int32
                                The value of the constant value is 1
                                This is an Parameter expression type
                                Type: System.Int32, Name: n, ByRef: False
                        This expression is a Lambda expression type
                        The name of the lambda is <null>
                        The return type is System.Int32
                        The expression has 2 arguments. They are:
                                This is an Parameter expression type
                                Type: System.Int32, Name: product, ByRef: False
                                This is an Parameter expression type
                                Type: System.Int32, Name: factor, ByRef: False
                        The expression body is:
                                This binary expression is a Multiply expression
                                The Left argument is:
                                        This is an Parameter expression type
                                        Type: System.Int32, Name: product, ByRef: False
                                The Right argument is:
                                        This is an Parameter expression type
                                        Type: System.Int32, Name: factor, ByRef: False

Erweitern Sie die BeispielbibliothekExtending the Sample Library

Die Beispiele in diesem Abschnitt zeigen die Kerntechniken, um Knoten in einer Ausdrucksbaumstruktur zu besuchen und zu untersuchen.The samples in this section show the core techniques to visit and examine nodes in an expression tree. Ich habe viele Aktionen ausgelassen, die Sie möglicherweise benötigen, um sich auf die wichtigsten Aufgaben beim Zugriff auf die Knoten in einer Ausdrucksbaumstruktur zu konzentrieren.I glossed over many actions you might need in order to concentrate on the core tasks of visiting and accessing nodes in an expression tree.

Erstens behandelt der Besucher nur Konstanten, die ganze Zahlen sind.First, the visitors only handle constants that are integers. Konstante Werte können jeder andere numerische Typ sein, und die C#-Sprache unterstützt Konvertierungen und Werbeaktionen zwischen diesen Typen.Constant values could be any other numeric type, and the C# language supports conversions and promotions between those types. Eine robustere Version dieses Codes würde alle diese Funktionen widerspiegeln.A more robust version of this code would mirror all those capabilities.

Sogar das letzte Beispiel erkennt eine Teilmenge der möglichen Knotentypen.Even the last example recognizes a subset of the possible node types. Sie können weiterhin viele Ausdrücke eingeben, die Fehler verursachen werden.You can still feed it many expressions that will cause it to fail. Eine vollständige Implementierung befindet sich in .NET Standard unter dem Namen ExpressionVisitor und kann die möglichen Knotentypen behandeln.A full implementation is included in the .NET Standard under the name ExpressionVisitor and can handle all the possible node types.

Schließlich wurde die Bibliothek, die ich in diesem Artikel verwendet habe, für Demo- und Lernzwecke erstellt.Finally, the library I used in this article was built for demonstration and learning. Sie ist nicht optimiert.It's not optimized. Ich habe sie geschrieben, um die verwendeten Strukturen klar zu machen und um die verwendeten Techniken für den Zugriff auf die Knoten hervorzuheben und zu analysieren, was sich dort befindet.I wrote it to make the structures used very clear, and to highlight the techniques used to visit the nodes and analyze what's there. Eine Produktionsimplementierung würde mehr Aufmerksamkeit auf die Leistung legen, als ich es habe.A production implementation would pay more attention to performance than I have.

Selbst mit diesen Einschränkungen sollten Sie sich auf dem richtigen Weg zum Schreiben von Algorithmen befinden, die Ausdrucksbaumstrukturen lesen und verstehen.Even with those limitations, you should be well on your way to writing algorithms that read and understand expression trees.

Weiter – Erstellen von AusdrückenNext -- Building Expressions