Bewerten Sie den Lambda-Ausdruck als Teil des Ausdrucksbaums

c# expression-trees lambda

Frage

Ich versuche, einen Lambda-Ausdruck mit Ausdrucksbäumen aufzubauen. Dies ist das Format des Lambda-Ausdrucks, den ich erstellen möchte:

Func<DateTime, string> requiredLambda = dt =>
    {
        var formattedDate = dt.ToShortDateString();

        /**
        * The logic is not important here - what is important is that I 
        * am using the result of the executed lambda expression.
        * */
        var builder = new StringBuilder();
        builder.Append(formattedDate);
        builder.Append(" Hello!");
        return builder.ToString();
    };

Der Catch ist, dass ich diesen Baum nicht von Grund auf neu Expression<Func<DateTime, string>> - die Formatierungslogik wird mir bereits in Form einer Instanz von Expression<Func<DateTime, string>> - sagen wir:

Expression<Func<DateTime, string>> formattingExpression = dt => dt.ToShortDateString();

Ich weiß, dass ich außerhalb des Ausdrucksbaumes anrufen kann

formattingExpression.Compile()(new DateTime(2003, 2, 1)) 

um den Ausdruck auszuwerten - aber das Problem besteht darin, dass ich es innerhalb des Ausdrucksbaums auswerten und zuweisen möchte - was mir erlaubt, zusätzliche Logik für das Ergebnis innerhalb des Ausdrucksbaums durchzuführen.

Nichts, was mir bisher eingefallen ist, scheint die Reise zu machen - fast sicher bis zu einem Missverständnis darüber, wie Expressionsbäume funktionieren. Jede Hilfe sehr geschätzt!

Akzeptierte Antwort

Also, wenn ich Sie richtig verstehe, wollen Sie einen Lambda (Ausdruck) erstellen, der Ihre übergebene Funktion verwendet und einige zusätzliche Arbeit um ihn herum ausführt. Sie möchten diese Funktion im Wesentlichen nur innerhalb eines Ausdrucks verwenden.

Lassen Sie mich an dieser Stelle vorschlagen, dass Sie nicht einmal Ausdrücke benutzen. Sie könnten einfach eine Funktion erstellen, die einen Func<DateTime, string> -Parameter verwendet und damit etwas verarbeitet. Aber wenn du den Ausdruck für etwas wirklich brauchst, werde ich einfach versuchen zu erklären, wie man einen baut.

In diesem Beispiel erstelle ich diese Funktion:

string MonthAndDayToString (int month, int day)
{
    return "'" + formattingFunction(new DateTime(2013, month, day)) + "'"
}

Wie Sie sehen, erstelle ich einen Func<int, int, string> der dann das DateTime Objekt erstellt und an die Funktion übergibt und dann das Ergebnis weiter ändert.

Func<DateTime, string> formatting = dt => (...) // as above

// define parameters for the lambda expression
ParameterExpression monthParam = Expression.Parameter(typeof(int));
ParameterExpression dayParam = Expression.Parameter(typeof(int));

// look up DateTime constructor
ConstructorInfo ci = typeof(DateTime).GetConstructor(new Type[] { typeof(int), typeof(int), typeof(int) });

// look up string.Concat
MethodInfo concat = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string), typeof(string) });

// inner call: formatting(new DateTime(2013, month, day))
var call = Expression.Call(formatting.Method, Expression.New(ci, Expression.Constant(2013), monthParam, dayParam));

// concat: "'" + call + "'"
var expr = Expression.Call(concat, Expression.Constant("'"), call, Expression.Constant("'"));

// create the final lambda: (int, int) => expr
var lambda = Expression.Lambda<Func<int, int, string>>(expr, new ParameterExpression[] { monthParam, dayParam });

// compile and execute
Func<int, int, string> func = lambda.Compile();
Console.WriteLine(func(2, 1)); // '01.02.2013 Hello!'
Console.WriteLine(func(11, 26)); // '26.11.2013 Hello!'

Nachdem ich Alexâ € ™ Antwort angeschaut habe, scheint es, als hätte ich deine Frage missverstanden und versucht, das Gegenteil von dem zu tun, was du tust. Aber es ist nicht allzu verschieden, es zu dem zu ändern, was Sie tatsächlich tun wollen:

Func<DateTime, string> formatting = dt => dt.ToShortDateString();

ParameterExpression param = Expression.Parameter(typeof(DateTime));
MethodInfo concat = typeof(string).GetMethod("Concat", new Type[] { typeof(string), typeof(string), typeof(string) });

var call = Expression.Call(formatting.Method, param);
var expr = Expression.Call(concat, Expression.Constant("'"), call, Expression.Constant(" Hello!'"));
var lambda = Expression.Lambda<Func<DateTime, string>>(expr, new ParameterExpression[] { param });

Func<DateTime, string> func = lambda.Compile();
Console.WriteLine(func(new DateTime(2013, 02, 01)));
Console.WriteLine(func(new DateTime(2013, 11, 26)));

Aber ich würde immer noch argumentieren, dass eine normale Funktion, die einen Func<DateTime, string> und einen DateTime Parameter Func<DateTime, string> DateTime einfacher zu verwalten wäre. Also, wenn Sie die Ausdrücke nicht wirklich brauchen , vermeiden Sie sie.


Warum ich immer noch nicht glaube, dass du dafür wirklich Ausdrücke brauchst. Betrachten Sie dieses Beispiel:

private Func<DateTime, string> formatting = dt => dt.ToShortDateString();
private Func<DateTime, string> formattingLogic = null;

public Func<DateTime, string> FormattingLogic
{
    get
    {
        if (formattingLogic == null)
        {
            // some results from reflection
            string word = "Hello";
            string quote = "'";

            formattingLogic = dt =>
            {
                StringBuilder str = new StringBuilder(quote);
                str.Append(formatting(dt));

                if (!string.IsNullOrWhiteSpace(word))
                    str.Append(" ").Append(word);

                str.Append(quote);
                return str.ToString();
            };
        }

        return formattingLogic;
    }
}

void Main()
{
    Console.WriteLine(FormattingLogic(new DateTime(2013, 02, 01))); // '01.02.2013 Hello'
    Console.WriteLine(FormattingLogic(new DateTime(2013, 11, 26))); // '26.11.2013 Hello'
}

Wie Sie sehen, konstruiere ich die Formatierungslogik nur einmal, wenn sie noch nicht eingestellt ist. Das ist der Zeitpunkt, an dem die Reflexion ausgeführt wird, um einige Werte zu erhalten, die Sie irgendwo in der Funktion verwenden. Da die Funktion als Lambda-Funktion erstellt wird, werden die Variablen, die wir aus dem lokalen Bereich innerhalb der Lambda-Funktion verwenden, automatisch erfasst und verfügbar gehalten.

Nun könnte man das natürlich auch als Ausdruck erstellen und die kompilierte Funktion speichern, aber ich würde argumentieren, dass es so viel lesbarer und wartbarer ist.


Beliebte Antwort

Sie können verwenden:

Func<Func<DateTime,string>, DateTime, string> requiredLambda = (f, dt) =>
{
    var formattedDate = f.Invoke(dt);

    /**
    * The logic is not important here - what is important is that I 
    * am using the result of the executed lambda expression.
    * */
    var builder = new StringBuilder();
    builder.Append(formattedDate);
    builder.Append(" Hello!");
    return builder.ToString();
};

Dann haben Sie Ihren Eingabeausdruck:

Expression<Func<DateTime, string>> formattingExpression = 
    dt => dt.ToShortDateString();

Und das Ergebnis:

var result = requiredLambda
    .Invoke(formattingExpression.Compile(), new DateTime(2003, 2, 1));

// 1.2.2003 Hello!


Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Ist diese KB legal? Ja, lerne warum