Ersetzen Sie den Parameter im Lambda-Ausdruck

c# expression-trees lambda partial-application

Frage

In Anbetracht dieses Codes:

public class Foo
{
    public int a { get; set; }
    public int b { get; set; }
}

private void Test()
{
    List<Foo> foos = new List<Foo>();
    foos.Add(new Foo());
    foos.Add(new Foo());
    Expression<Func<Foo, int>> exp0 = f => f.a * f.b;
    Expression<Func<int>> exp1 = () => foos[0].a * foos[0].b;
    Expression<Func<int>> exp2 = () => foos[1].a * foos[1].b;
}

Wie kannst du exp0 nehmen und es in zwei Ausdrücke exp1 mit exp1 und exp2 identisch exp2 . Beachte, dass ich exp0 für jeden Foo in foos evaluieren foos , sondern stattdessen zwei neue Ausdrücke bekomme.

[Update] :

Grundsätzlich möchte ich in der Lage sein, einen Ausdruck, der an eine Linq Erweiterungsmethode wie Sum in einen Ausdruck pro Element in der Enumeration zu erweitern oder zu Linq da diese Enumerationen statisch sind und weil ich bereits Code habe, der Ausdrücke liest, die don nimm Parameter (und mache sie dann zu einer anderen Sprache).

Ich verwende den MetadataToken als Verweise auf Eigenschaften, die ein bestimmtes Attribut haben (in diesem Fall hätte a und b dieses Attribut) und benutze es mit einem Dictionary, das C # -Eigenschaften mit den Variablen einer anderen Sprache korreliert:

Foo foo = new Foo();
Expression<Func<int>> exp = () => foo.a * foo.a + foo.b;
string result1 = GetResult(exp); // gets "v_001 * v_001 + v_002"

List<Foo> foes = new List<Foo>();
foes.Add(new Foo());
foes.Add(new Foo());
Expression<Func<int>> exp2 = () => foes.Sum(f => f.a * f.a + f.b);
string result2 = GetResult(exp2); // should get "(v_001 * v_001 + v_002) + (v_003 * v_003 + v_004)"

Akzeptierte Antwort

Ich würde es so machen:

Schreiben Sie einen Parameter-Replacer-Ausdruck-Besucher, der den ursprünglichen Ausdruck wie folgt manipuliert:

  1. Entfernt den Parameter, den Sie nicht vollständig aus der Lambda-Signatur möchten.
  2. Ersetzt alle Verwendungen des Parameters durch den gewünschten Indexerausdruck.

Hier ist eine schnelle und dreckige Probe, die ich auf Grundlage meiner früheren Antwort auf eine andere Frage aufgepeppt habe:

public static class ParameterReplacer
{
    // Produces an expression identical to 'expression'
    // except with 'source' parameter replaced with 'target' expression.     
    public static Expression<TOutput> Replace<TInput, TOutput>
                    (Expression<TInput> expression,
                    ParameterExpression source,
                    Expression target)
    {
        return new ParameterReplacerVisitor<TOutput>(source, target)
                    .VisitAndConvert(expression);
    }

    private class ParameterReplacerVisitor<TOutput> : ExpressionVisitor
    {
        private ParameterExpression _source;
        private Expression _target;

        public ParameterReplacerVisitor
                (ParameterExpression source, Expression target)
        {
            _source = source;
            _target = target;
        }

        internal Expression<TOutput> VisitAndConvert<T>(Expression<T> root)
        {
            return (Expression<TOutput>)VisitLambda(root);
        }

        protected override Expression VisitLambda<T>(Expression<T> node)
        {
            // Leave all parameters alone except the one we want to replace.
            var parameters = node.Parameters
                                 .Where(p => p != _source);

            return Expression.Lambda<TOutput>(Visit(node.Body), parameters);
        }

        protected override Expression VisitParameter(ParameterExpression node)
        {
            // Replace the source with the target, visit other params as usual.
            return node == _source ? _target : base.VisitParameter(node);
        }
    }
}

Verwendung für Ihr Szenario (getestet):

var zeroIndexIndexer = Expression.MakeIndex
        (Expression.Constant(foos),
         typeof(List<Foo>).GetProperty("Item"), 
         new[] { Expression.Constant(0) });


// .ToString() of the below looks like the following: 
//  () =>    (value(System.Collections.Generic.List`1[App.Foo]).Item[0].a
//         *  value(System.Collections.Generic.List`1[App.Foo]).Item[0].b)
var exp1Clone = ParameterReplacer.Replace<Func<Foo, int>, Func<int>>
                  (exp0, exp0.Parameters.Single(), zeroIndexIndexer);


Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow
Lizenziert unter: CC-BY-SA with attribution
Nicht verbunden mit Stack Overflow