2017-07-05 3 views
2

Considérons deux arbres d'expression:arbres d'expression - expression substitut

Expression<Func<float, float, float>> f1 = (x, y) => x + y; 
Expression<Func<float, float>> f2 = x => x * x; 

Je veux remplacer l'expression f2 comme second paramètre de f1 et obtenir l'expression suivante:

Expression<Func<float, float, float>> f3 = (x, y) => x + y * y; 

La façon la plus simple est pour utiliser Expression.Lambda et Expression.Invoke, mais le résultat ressemblera à

(x, y) => f1(x, f2(y)) 

Mais cela est inacceptable pour moi en raison des limitations ORM qui ne peuvent pas gérer invoke/lambda correctement.

Est-il possible de construire l'expression sans traversée complète des arbres d'expression? Un exemple de travail qui répondent à mes besoins peut être trouvé here mais je veux une solution plus simple.

+0

Sans plein traversal de arbres d'expression - non. Fondamentalement, vous avez besoin de remplacer le paramètre, qui est implémenté avec 'ExpressionVisitor'. –

Répondre

2

Vous ne pouvez pas le faire sans traversée complète des deux expressions. Heureusement, ExpressionVisitor fait plein traversal vraiment facile:

class ReplaceParameter : ExpressionVisitor { 
    private readonly Expression replacement; 
    private readonly ParameterExpression parameter; 
    public ReplaceParameter(
     ParameterExpression parameter 
    , Expression replacement 
    ) { 
     this.replacement = replacement; 
     this.parameter = parameter; 
    } 
    protected override Expression VisitParameter(ParameterExpression node) { 
     return node == parameter ? replacement : node; 
    } 
} 

Utilisez ce visiteur deux fois pour terminer le remplacement:

Expression<Func<float,float,float>> f1 = (x, y) => x + y; 
Expression<Func<float,float>> f2 = x => x * x; 
var pX = f2.Parameters[0]; 
var pY = f1.Parameters[1]; 
var replacerF2 = new ReplaceParameter(pX, pY); 
var replacerF1 = new ReplaceParameter(pY, replacerF2.Visit(f2.Body)); 
var modifiedF1 = Expression.Lambda(
    replacerF1.Visit(f1.Body) 
, f1.Parameters 
); 
Console.WriteLine(modifiedF1); 

Les impressions ci-dessus

(x, y) => (x + (y * y)) 

Demo.