Looking for c# Keywords? Try Ask4Keywords

C# LanguageAusdrucksbäume


Einführung

Ausdrucksbäume sind in einer baumähnlichen Datenstruktur angeordnete Ausdrücke. Jeder Knoten im Baum ist eine Darstellung eines Ausdrucks, wobei ein Ausdruck Code ist. Eine In-Memory-Darstellung eines Lambda-Ausdrucks wäre ein Ausdrucksbaum, der die tatsächlichen Elemente (dh Code) der Abfrage enthält, jedoch nicht das Ergebnis. Ausdrucksbäume machen die Struktur eines Lambda-Ausdrucks transparent und explizit.

Syntax

  • Ausdruck <TDelegate> name = lambdaExpression;

Parameter

Parameter Einzelheiten
TDelegate Der Delegattyp, der für den Ausdruck verwendet werden soll
LambdaExpression Der Lambda-Ausdruck (zB num => num < 5 )

Bemerkungen

Intro zu Ausdrucksbäumen

Woher wir kamen

In Ausdrucksbäumen wird zur Laufzeit "Quellcode" verwendet. decimal CalculateTotalTaxDue(SalesOrder order) Sie sich eine Methode vor, die die auf eine decimal CalculateTotalTaxDue(SalesOrder order) fällige Umsatzsteuer decimal CalculateTotalTaxDue(SalesOrder order) . Die Verwendung dieser Methode in einem .NET-Programm ist einfach - Sie nennen es einfach decimal taxDue = CalculateTotalTaxDue(order); . Was ist, wenn Sie es auf alle Ergebnisse einer Remote-Abfrage anwenden möchten (SQL, XML, Remote-Server usw.)? Diese entfernten Abfragequellen können die Methode nicht aufrufen! In all diesen Fällen müssten Sie normalerweise den Fluss umkehren. Machen Sie die gesamte Abfrage, speichern Sie sie im Arbeitsspeicher, durchlaufen Sie die Ergebnisse und berechnen Sie die Steuer für jedes Ergebnis.

So vermeiden Sie die Speicher- und Latenzprobleme der Flussumkehrung

Ausdrucksbäume sind Datenstrukturen in einem Format eines Baums, wobei jeder Knoten einen Ausdruck enthält. Sie werden verwendet, um die kompilierten Anweisungen (wie zum Filtern von Daten verwendete Methoden) in Ausdrücke zu übersetzen, die außerhalb der Programmumgebung verwendet werden könnten, beispielsweise innerhalb einer Datenbankabfrage.

Das Problem hierbei ist, dass eine Remote-Abfrage nicht auf unsere Methode zugreifen kann . Wir könnten dieses Problem vermeiden, wenn wir stattdessen die Anweisungen für die Methode an die Fernabfrage senden. In unserem CalculateTotalTaxDue Beispiel bedeutet das, dass wir diese Informationen senden:

  1. Erstellen Sie eine Variable, um die Gesamtsteuer zu speichern
  2. Durchlaufen Sie alle Zeilen der Bestellung
  3. Prüfen Sie für jede Zeile, ob das Produkt steuerpflichtig ist
  4. Ist dies der Fall, multiplizieren Sie die Zeilensumme mit dem anwendbaren Steuersatz und addieren Sie diesen Betrag zur Gesamtsumme
  5. Sonst nichts tun

Mit diesen Anweisungen kann die Remote-Abfrage die Arbeit ausführen, während sie die Daten erstellt.

Es gibt zwei Herausforderungen, um dies umzusetzen. Wie wandeln Sie eine kompilierte .NET-Methode in eine Liste von Anweisungen um und wie formatieren Sie die Anweisungen so, dass sie vom Remote-System verwendet werden können?

Ohne Ausdrucksbäume konnten Sie nur das erste Problem mit MSIL lösen. (MSIL ist der Assembler-artige Code, der vom .NET-Compiler erstellt wird.) Das Analysieren von MSIL ist möglich , aber nicht einfach. Selbst wenn Sie es richtig analysieren, kann es schwierig sein, die Absicht des ursprünglichen Programmierers mit einer bestimmten Routine zu bestimmen.

Ausdrucksbäume retten den Tag

Ausdrucksbäume behandeln genau diese Probleme. Sie stellen Programmanweisungen in einer Baumdatenstruktur dar, wobei jeder Knoten eine Anweisung darstellt und Verweise auf alle Informationen enthält, die Sie zur Ausführung dieser Anweisung benötigen. Eine MethodCallExpression hat beispielsweise einen Verweis auf 1) die MethodInfo , die aufgerufen werden soll, 2) eine Liste von Expression , die an diese Methode übergeben wird, 3) für Instanzmethoden und den Expression , für den Sie die Methode aufrufen. Sie können "durch den Baum gehen" und die Anweisungen auf Ihre Remote-Abfrage anwenden.

Ausdrucksbäume erstellen

Die einfachste Möglichkeit zum Erstellen eines Ausdrucksbaums besteht in einem Lambda-Ausdruck. Diese Ausdrücke sehen fast genauso aus wie normale C # -Methoden. Es ist wichtig zu wissen, dass dies Compiler-Magie ist . Wenn Sie zum ersten Mal einen Lambda-Ausdruck erstellen, prüft der Compiler, wozu Sie ihn zuweisen. Wenn es ein ist Delegate - Typ (einschließlich Action oder Func ), wandelt der Compiler den Lambda - Ausdruck in einen Delegierten. Wenn es sich um eine LambdaExpression (oder um einen Expression<Action<T>> oder einen Expression<Func<T>> dem es sich stark um LambdaExpression - LambdaExpression ), wandelt der Compiler ihn in eine LambdaExpression . Hier setzt die Magie an. Hinter den Kulissen verwendet der Compiler die Ausdrucksstruktur-API, um Ihren Lambda-Ausdruck in eine LambdaExpression .

Lambda-Ausdrücke können nicht jede Art von Ausdrucksbaum erstellen. In diesen Fällen können Sie die Expressions-API manuell verwenden, um die Baumstruktur zu erstellen, die Sie benötigen. Im Beispiel für das Verständnis der Ausdrücke-API erstellen wir den CalculateTotalSalesTax Ausdruck mithilfe der API.

HINWEIS: Die Namen werden hier etwas verwirrend. Ein Lambda-Ausdruck (zwei Wörter, Kleinbuchstaben) bezieht sich auf den Codeblock mit einem Indikator => . Es stellt eine anonyme Methode in C # dar und wird entweder in einen Delegate oder einen Expression konvertiert. Eine LambdaExpression (ein Wort, PascalCase) bezieht sich auf den Knotentyp innerhalb der Ausdruck-API, der eine Methode darstellt, die Sie ausführen können.

Ausdrucksbäume und LINQ

Eine der häufigsten Anwendungen von Ausdrucksbäumen ist bei LINQ- und Datenbankabfragen. LINQ paart eine Ausdrucksbaumstruktur mit einem Abfrageanbieter, um Ihre Anweisungen auf die Remote-Zielabfrage anzuwenden. Beispielsweise wandelt der Abfrage-Provider LINQ to Entity Framework einen Ausdrucksbaum in SQL um, der direkt für die Datenbank ausgeführt wird.

Wenn Sie alle Teile zusammenfügen, können Sie die wahre Stärke von LINQ erkennen.

  1. Schreiben Sie eine Abfrage mit einem Lambda-Ausdruck: products.Where(x => x.Cost > 5)
  2. Der Compiler wandelt diesen Ausdruck in einen Ausdrucksbaum mit den Anweisungen "prüfe, ob die Cost-Eigenschaft des Parameters größer als fünf ist".
  3. Der Abfrageanbieter analysiert die Ausdrucksbaumstruktur und erstellt eine gültige SQL-Abfrage SELECT * FROM products WHERE Cost > 5
  4. Der ORM projiziert alle Ergebnisse in POCOs und Sie erhalten eine Liste der Objekte zurück

Anmerkungen

  • Ausdrucksbäume sind unveränderlich. Wenn Sie einen Ausdrucksbaum ändern möchten, müssen Sie einen neuen erstellen, den vorhandenen in den neuen kopieren (um einen Ausdrucksbaum zu durchsuchen, können Sie den ExpressionVisitor ) und die gewünschten Änderungen vornehmen.

Ausdrucksbäume Verwandte Beispiele