Ich bin der Dummy in diesem Szenario.
Ich habe versucht, bei Google zu lesen, was das ist, aber ich verstehe es einfach nicht. Kann mir jemand eine einfache Erklärung geben, was sie sind und warum sie nützlich sind?
Bearbeiten: Ich spreche über die LINQ-Funktion in .Net.
Antworten:
Die beste Erklärung für Ausdrucksbäume, die ich jemals gelesen habe, ist dieser Artikel von Charlie Calvert.
Etwas zusammenfassen;
Ein Ausdrucksbaum repräsentiert, was Sie tun möchten, nicht wie Sie es tun möchten.
Dies ist der wichtigste Unterschied zwischen Delegaten und Ausdrücken. Sie rufen
function
(aFunc<int, int, int>
) auf, ohne jemals zu wissen, was mit den beiden von Ihnen übergebenen Ganzzahlen geschehen soll. Es dauert zwei und gibt eins zurück, das ist das Beste, was Ihr Code wissen kann.Nun, im Gegensatz zu Delegierten, Ihr Code kann wissen , was ein Ausdruck Baum zu tun gemeint ist.
Das heißt, Sie können nicht einfach einen Ausdrucksbaum aufrufen, wie Sie einen Delegaten aufrufen könnten, sondern Sie können ihn analysieren. Was kann Ihr Code also verstehen, wenn Sie die Variable analysieren
expression
?// `expression.NodeType` returns NodeType.Lambda. // `expression.Type` returns Func<int, int, int>. // `expression.ReturnType` returns Int32. var body = expression.Body; // `body.NodeType` returns ExpressionType.Add. // `body.Type` returns System.Int32. var parameters = expression.Parameters; // `parameters.Count` returns 2. var firstParam = parameters[0]; // `firstParam.Name` returns "a". // `firstParam.Type` returns System.Int32. var secondParam = parameters[1]. // `secondParam.Name` returns "b". // `secondParam.Type` returns System.Int32.
Hier sehen wir, dass es viele Informationen gibt, die wir aus einem Ausdruck erhalten können.
Aber warum sollten wir das brauchen?
Wieder einmal sehen wir, dass die Ausdrucksbäume es uns ermöglichen, darzustellen (auszudrücken?), Was wir tun möchten. Und wir verwenden Übersetzer, die entscheiden, wie unsere Ausdrücke verwendet werden.
quelle
Ein Ausdrucksbaum ist ein Mechanismus zum Übersetzen von ausführbarem Code in Daten. Mithilfe eines Ausdrucksbaums können Sie eine Datenstruktur erstellen, die Ihr Programm darstellt.
In C # können Sie mit dem von Lambda-Ausdrücken erzeugten Ausdrucksbaum mithilfe der
Expression<T>
Klasse arbeiten.In einem herkömmlichen Programm schreiben Sie Code wie folgt:
double hypotenuse = Math.Sqrt(a*a + b*b);
Dieser Code veranlasst den Compiler, eine Zuweisung zu generieren, und das war's. In den meisten Fällen ist das alles, was Sie interessiert.
Mit herkömmlichem Code kann Ihre Anwendung nicht rückwirkend zurückgehen und überprüfen
hypotenuse
, ob sie durch Ausführen einesMath.Sqrt()
Aufrufs erstellt wurde. Diese Informationen sind einfach nicht Teil dessen, was enthalten ist.Betrachten Sie nun einen Lambda-Ausdruck wie den folgenden:
Func<int, int, double> hypotenuse = (a, b) => Math.Sqrt(a*a + b*b);
Das ist etwas anders als zuvor. Jetzt
hypotenuse
ist eigentlich ein Verweis auf einen Block von ausführbarem Code . Wenn Sie anrufenhypotenuse(3, 4);
Sie erhalten den zurückgegebenen Wert
5
.Wir können Ausdrucksbäume verwenden , um den Block ausführbaren Codes zu untersuchen, der erzeugt wurde. Versuchen Sie stattdessen Folgendes:
Expression<Func<int, int, int>> addTwoNumbersExpression = (x, y) => x + y; BinaryExpression body = (BinaryExpression) addTwoNumbersExpression.Body; Console.WriteLine(body);
Dies erzeugt:
Fortgeschrittenere Techniken und Manipulationen sind mit Ausdrucksbäumen möglich.
quelle
Ausdrucksbäume sind eine speicherinterne Darstellung eines Ausdrucks, z. B. ein arithmetischer oder boolescher Ausdruck. Betrachten Sie zum Beispiel den arithmetischen Ausdruck
a + b*2
Da * eine höhere Operatorrangfolge als + hat, wird der Ausdrucksbaum folgendermaßen erstellt:
[+] / \ a [*] / \ b 2
Mit diesem Baum kann er für alle Werte von a und b ausgewertet werden. Darüber hinaus können Sie es in andere Ausdrucksbäume umwandeln, um beispielsweise den Ausdruck abzuleiten.
Wenn Sie einen Ausdruck Baum implementieren, würde ich vorschlagen , eine Basisklasse erstellen Expression . Daraus abgeleitet würde die Klasse BinaryExpression für alle binären Ausdrücke wie + und * verwendet. Dann könnten Sie eine VariableReferenceExpression in Referenzvariablen (wie a und b) und eine andere Klasse ConstantExpression (für die 2 aus dem Beispiel) einführen .
Der Ausdrucksbaum wird in vielen Fällen als Ergebnis der Analyse einer Eingabe erstellt (direkt vom Benutzer oder aus einer Datei). Für die Auswertung des Ausdrucksbaums würde ich empfehlen, das Besuchermuster zu verwenden .
quelle
Kurze Antwort: Es ist schön, dieselbe Art von LINQ-Abfrage schreiben und auf eine beliebige Datenquelle verweisen zu können. Sie könnten keine "Language Integrated" -Abfrage ohne sie haben.
Lange Antwort: Wie Sie wahrscheinlich wissen, transformieren Sie den Quellcode beim Kompilieren von einer Sprache in eine andere. Normalerweise von einer Hochsprache (C #) bis zu einem niedrigeren Hebel (IL).
Grundsätzlich gibt es zwei Möglichkeiten, dies zu tun:
Letzteres ist das, was alle Programme, die wir als "Compiler" kennen, tun.
Sobald Sie einen Analysebaum haben, können Sie ihn problemlos in eine andere Sprache übersetzen. Dies ermöglichen uns Ausdrucksbäume. Da der Code als Daten gespeichert ist, können Sie alles tun, was Sie wollen, aber wahrscheinlich möchten Sie ihn nur in eine andere Sprache übersetzen.
In LINQ to SQL werden die Ausdrucksbäume nun in einen SQL-Befehl umgewandelt und dann über die Leitung an den Datenbankserver gesendet. Soweit ich weiß, machen sie beim Übersetzen des Codes nichts wirklich Besonderes, aber sie könnten es . Beispielsweise könnte der Abfrageanbieter abhängig von den Netzwerkbedingungen unterschiedlichen SQL-Code erstellen.
quelle
IIUC, ein Ausdrucksbaum ähnelt einem abstrakten Syntaxbaum, aber ein Ausdruck ergibt normalerweise einen einzelnen Wert, während ein AST ein gesamtes Programm darstellen kann (mit Klassen, Paketen, Funktionen, Anweisungen usw.).
Für den Ausdruck (2 + 3) * 5 lautet der Baum jedenfalls:
* / \ + 5 / \ 2 3
Bewerten Sie jeden Knoten rekursiv (von unten nach oben), um den Wert am Wurzelknoten zu erhalten, dh den Wert des Ausdrucks.
Sie können natürlich auch unäre (Negation) oder trinäre (Wenn-Dann-Sonst) Operatoren und Funktionen (n-ary, dh eine beliebige Anzahl von Operationen) haben, wenn Ihre Ausdruckssprache dies zulässt.
Die Bewertung von Typen und die Typsteuerung erfolgt über ähnliche Bäume.
quelle
Die DLR-
Ausdrucksbäume sind eine Ergänzung zu C #, um die Dynamic Language Runtime (DLR) zu unterstützen. Das DLR ist auch dafür verantwortlich, uns die "var" -Methode zur Deklaration von Variablen zu geben. (
var objA = new Tree();
)Mehr zum DLR .
Im Wesentlichen wollte Microsoft die CLR für dynamische Sprachen wie LISP, SmallTalk, Javascript usw. öffnen. Dazu mussten sie in der Lage sein, Ausdrücke im laufenden Betrieb zu analysieren und auszuwerten. Das war vor dem DLR nicht möglich.
Zurück zu meinem ersten Satz: Ausdrucksbäume sind eine Ergänzung zu C #, die die Möglichkeit eröffnet, das DLR zu verwenden. Zuvor war C # eine viel statischere Sprache - alle Variablentypen mussten als bestimmter Typ deklariert und der gesamte Code zur Kompilierungszeit geschrieben werden.
Die Verwendung mit Datenausdrucksbäumen
öffnet die Schleusentore für dynamischen Code.
Angenommen, Sie erstellen eine Immobilienseite. Während der Entwurfsphase kennen Sie alle Filter, die Sie anwenden können. Um diesen Code zu implementieren, haben Sie zwei Möglichkeiten: Sie können eine Schleife schreiben, die jeden Datenpunkt mit einer Reihe von Wenn-Dann-Prüfungen vergleicht. Sie können auch versuchen, eine Abfrage in einer dynamischen Sprache (SQL) zu erstellen und diese an ein Programm weiterzuleiten, das die Suche für Sie (die Datenbank) durchführen kann.
Mit Ausdrucksbäumen können Sie jetzt den Code in Ihrem Programm im laufenden Betrieb ändern und die Suche durchführen. Insbesondere können Sie dies über LINQ tun.
(Weitere Informationen : MSDN: Gewusst wie: Verwenden von Ausdrucksbäumen zum Erstellen dynamischer Abfragen ).
Über Daten hinaus
Die Hauptverwendung für Ausdrucksbäume ist die Verwaltung von Daten. Sie können jedoch auch für dynamisch generierten Code verwendet werden. Wenn Sie also eine dynamisch definierte Funktion (z. B. Javascript) möchten, können Sie einen Ausdrucksbaum erstellen, kompilieren und die Ergebnisse auswerten.
Ich würde etwas tiefer gehen, aber diese Seite macht einen viel besseren Job:
Ausdrucksbäume als Compiler
Zu den aufgeführten Beispielen gehören das Erstellen generischer Operatoren für Variablentypen, das manuelle Rollen von Lambda-Ausdrücken, das flache Klonen mit hoher Leistung und das dynamische Kopieren von Lese- / Schreibeigenschaften von einem Objekt in ein anderes.
Zusammenfassung
Ausdrucksbäume sind Darstellungen von Code, der zur Laufzeit kompiliert und ausgewertet wird. Sie ermöglichen dynamische Typen, was für die Datenmanipulation und die dynamische Programmierung nützlich ist.
quelle
var
ist ein syntaktischer Zucker zur Kompilierungszeit - er hat nichts mit Ausdrucksbäumen, DLR oder der Laufzeit zu tun.var i = 0
wird so kompiliert, als ob Sie geschrieben hättenint i = 0
, sodass Sie keinenvar
Typ darstellen können, der in der Kompilierungszeit nicht bekannt ist. Ausdrucksbäume sind keine "Ergänzung zur Unterstützung des DLR", sondern werden in .NET 3.5 eingeführt, um LINQ zu ermöglichen. DLR hingegen wird in .NET 4.0 eingeführt, um dynamische Sprachen (wie IronRuby) und dasdynamic
Schlüsselwort zuzulassen . Ausdrucksbäume werden vom DLR tatsächlich zur Bereitstellung von Interop verwendet, nicht umgekehrt.Ist der Ausdrucksbaum, auf den Sie sich beziehen, ein Ausdrucksbewertungsbaum?
Wenn ja, handelt es sich um einen vom Parser erstellten Baum. Parser verwendete den Lexer / Tokenizer, um die Token aus dem Programm zu identifizieren. Parser erstellt den Binärbaum aus den Token.
Hier ist die detaillierte Erklärung
quelle