Frage

Angenommen, ich habe die folgenden Ausdrücke:

Expression<Action<T, StringBuilder>> expr1 = (t, sb) => sb.Append(t.Name);
Expression<Action<T, StringBuilder>> expr2 = (t, sb) => sb.Append(", ");
Expression<Action<T, StringBuilder>> expr3 = (t, sb) => sb.Append(t.Description);

Ich möchte in der Lage sein, diese in einem Verfahren zu kompilieren / delegieren entspricht dem folgenden:

void Method(T t, StringBuilder sb) 
{
    sb.Append(t.Name);
    sb.Append(", ");
    sb.Append(t.Description);
}

Was ist der beste Weg, dies zu nähern? Ich möchte es eine gute Leistung, idealerweise mit Leistung entsprechend dem obigen Verfahren.

UPDATE So, während es scheint, dass es keine Möglichkeit gibt, diese direkt in C # zu tun, 3 Gibt es eine Möglichkeit, einen Ausdruck zu IL zu konvertieren, so dass ich es mit System.Reflection.Emit verwenden kann?

War es hilfreich?

Lösung

Leider gibt es in .NET 3.5 Sie können einen Ausdruck erstellen, die eine Reihe von willkürlichen Operationen durchführt. Hier ist die Liste der unterstützten Ausdrücke:

  • Arithmetic: Hinzufügen, AddChecked, Dividieren, Modulo, Multiplizieren, MultiplyChecked, Negieren, NegateChecked, Macht, subtrahieren, SubtractChecked, UnaryPlus
  • Creation: Bind, ElementInit, ListBind, ListInit, MemberBind, MemberInit, Neu, NewArrayBounds, NewArrayInit
  • Bitwise: Und ExclusiveOr, Leftshift (<<), nicht oder, RightShift (>>)
  • Logisch: AndAlso (&&), Zustand (:), Equal, GreaterThan, GreaterThanOrEqual, LessThan, * LessThanOrEqual, Ungleich, OrElse (||), TypeIs
  • Mitglied Zugang: Arrayindex, ArrayLength, Rufen, Feld, Eigentum, PropertyOrField
  • übriges: Konvertieren, ConvertChecked, Coalesce (??), Constant, Invoke, Lambda, Parameter, TypeAs, Quote

.NET 4 erweitert diese API durch die folgenden Ausdrücke hinzufügen:

  • Mutation: AddAssign, AddAssignChecked, AndAssign, Assign, DivideAssign, ExclusiveOrAssign, LeftShiftAssign, ModuloAssign, MultiplyAssign, MultiplyAssignChecked, OrAssign, PostDecrementAssign, PostIncrementAssign, PowerAssign, PreDecrementAssign, PreIncrementAssign, RightShiftAssign, SubtractAssign, SubtractAssignChecked
  • Arithmetik: Decrement, Standard-Schritte, OnesComplement
  • Mitglied Zugang: Arrayaccess, Dynamische
  • Logisch: ReferenceEqual, ReferenceNotEqual, TypeEqual
  • Durchfluss: Block, Pause, Weiter, Leer, Goto, IfThen, IfThenElse, IfFalse, iftrue, Etikett, Loop, Return, Switch, Schaltschrankbau, Unbox, Variable
  • Ausnahmen: Catch, Rethrow, Werfen
  • Debug: ClearDebugInfo, Debuginfo

Die blockieren Ausdruck ist besonders interessant.

Andere Tipps

Sie können, aber es ist nicht triviale Aufgabe.

Wenn Sie eine Variable vom Typ Expression haben, können Sie die Body-Eigenschaft überprüfen die Datenstruktur des Ausdrucks zu finden.

Sie können nicht den Compiler bitten, es für Sie zu kompilieren, weil es nicht das Ergebnis erhalten, die Sie wollen. Hier finden Sie die Körper aller Ihre Ausdrücke analysieren müssen und sie irgendwie zu einem einzigen Verfahren zu kombinieren, die alle von IL zugleich emittiert (oder durch C # produziert und haben, dass nicht kompiliert, wenn Sie das Gefühl, IL ist ein Schritt zu weit).

So wie LINQ to SQL kompiliert den Ausdruck in eine SQL-Abfrage, so können Sie Ihre Ausdrücke in kompilieren, was Sie brauchen. Sie werden eine Menge Arbeit vor Ihnen, aber Sie nur das umsetzen müssen, was Sie wollen unterstützen.

In diesem eher trivialen Fall, dass ich nicht denke, es ist notwendig, um Ihre eigenen LINQ-Provider zu erstellen. Sie könnten nur mit dem Ausdruck bestanden, arbeiten und gehen von dort aus. Aber ich vermute, Ihre Anwendung ein wenig komplizierter ist als das.

4.0 ist dies viel einfacher, dank die Unterstützung für Blockoperationen im Baum (wenn auch nicht in dem C # Compiler Ausdruck).

Allerdings könnten Sie dies tun, indem die Tatsache ausgenutzt, dass StringBuilder eine „fließend“ API zeigt; so statt Action<T,StringBuilder> Sie eine Func<T,StringBuilder,StringBuilder> haben - wie unten (beachten Sie, dass die tatsächliche Syntax für diese Ausdrücke auszudrücken, ist identisch in diesem Fall):

class Program
{
    static void Main()
    {
        Foo(new MyType { Name = "abc", Description = "def" });
    }
    static void Foo<T>(T val) where T : IMyType
    {
        var expressions = new Expression<Func<T, StringBuilder, StringBuilder>>[] {
                (t, sb) => sb.Append(t.Name),
                (t, sb) => sb.Append(", "),
                (t, sb) => sb.Append(t.Description)
        };
        var tparam = Expression.Parameter(typeof(T), "t");
        var sbparam = Expression.Parameter(typeof(StringBuilder), "sb");

        Expression body = sbparam;
        for (int i = 0; i < expressions.Length; i++)
        {
            body = Expression.Invoke(expressions[i], tparam, body);
        }
        var func = Expression.Lambda<Func<T, StringBuilder, StringBuilder>>(
            body, tparam, sbparam).Compile();

        // now test it
        StringBuilder sbInst = new StringBuilder();
        func(val, sbInst);
        Console.WriteLine(sbInst.ToString());
    }
}
public class MyType : IMyType
{
    public string Name { get; set; }
    public string Description { get; set; }
}
interface IMyType
{
    string Name { get; }
    string Description { get; }
}

Es ist sicher möglich , um die Bäume zu inspizieren und zu emittieren IL manuell (DynamicMethod vielleicht), aber Sie würden einige Entscheidungen über die Begrenzung der Komplexität machen. Für den Code wie dargestellt ich es tun konnte in vernünftig Zeit (noch nicht trivial), aber wenn Sie erwarten, etwas komplexere Expression ist mehr Sie gebraten.

Sie können nur das 4. Leider in .NET tun nicht die Details kennen.

Edit:

Wenn Sie mit Reflection.Emit bequem sind, können Sie eine Methode emittieren könnte, diese Ausdrücke in Folge aufgerufen wird.

Eine weitere Alternative:

Erstellen Sie eine 'do' Methode, das heißt:

void Do(params Action[] actions)
{
  foreach (var a in actions) a();
}

Eine weitere Möglichkeit, auf diesem Problem zu suchen ist daran zu erinnern, dass die Delegierten Multi-Cast sind; Sie können eine Action oft kombinieren;

class Program
{
    static void Main()
    {
        Foo(new MyType { Name = "abc", Description = "def" });
    }

    static void Foo<T>(T val) where T : IMyType {
        var expressions = new Expression<Action<T, StringBuilder>>[] {
                (t, sb) => sb.Append(t.Name),
                (t, sb) => sb.Append(", "),
                (t, sb) => sb.Append(t.Description)
        };
        Action<T, StringBuilder> result = null;
        foreach (var expr in expressions) result += expr.Compile();
        if (result == null) result = delegate { };
        // now test it
        StringBuilder sbInst = new StringBuilder();
        result(val, sbInst);
        Console.WriteLine(sbInst.ToString());
    }
}
public class MyType : IMyType
{
    public string Name { get; set; }
    public string Description { get; set; }
}
interface IMyType
{
    string Name { get; }
    string Description { get; }

}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top