2013-07-31 1 views
8

Eingabe Wenn ich einen Ausdruck einer Funktion delegieren, die wie so eine Reihe von Parametern nimmt:einen Ausdruck reduzieren, indem ein Parameter

Expression<Func<int, int, int, bool>> test = (num1, num2, num3) => num1 + num2 == num3; 

ist es eine Möglichkeit,/wie kann ich einen der Werte ersetzen (zB 5 für num1) und erhalten den entsprechenden Ausdruck:

Expression<Func<int, int, bool>> test = (num2, num3) => 5 + num2 == num3; 

EDIT:

muss auch komplexe Typen lösen, zum Beispiel:

Expression<Func<Thing, int, int>> test = (thing, num2) => thing.AnIntProp + num2; 
+0

möglich duplicate von [C# Linq vs. Curry] (http://stackoverflow.com/questions/8826266/c-sharp-linq-vs-currying) –

+2

während ich wählte es ist dup, kann diese Frage tatsächlich anders sein . Vielleicht suchen Sie die Verwendung von Ausdruck Besucher, um Argument mit Wert zu ersetzen? (etwas in der Art von [dies] (http://stackoverflow.com/questions/11164009/using-a-linq-expressionvisitor-to-replace-primitive-parameters-with-property-ref)) –

+0

Nicht ganz. Ich möchte mit Ausdrücken arbeiten. Ich denke, es ist näher (http://stackoverflow.com/questions/11159697/replace-parameter-in-lambda-expression) oder [dies] (http://stackoverflow.com/questions/5631070/currying) -ausdrücken-in-c-sharp) –

Antwort

2

Meine Antwort war, einen Ausdruck Besucher zu verwenden. (Danke @ Alexei-Levenkov für das Aufzeigen).

Die Antwort für meine spezielle Situation war ein wenig anders als für das vereinfachte Beispiel, das ich in der Frage verwendete. Aber für die Vollständigkeit, hier war, wie ich es tat:

public class ResolveParameterVisitor : ExpressionVisitor 
{ 
    private readonly ParameterExpression _param; 
    private readonly object _value; 

    public ResolveParameterVisitor(ParameterExpression param, object value) 
    { 
     _param = param; 
     _value = value; 
    } 

    public Expression ResolveLocalValues(Expression exp) 
    { 
     return Visit(exp); 
    } 

    protected override Expression VisitParameter(ParameterExpression node) 
    { 
     if (node.Type == _param.Type && node.Name == _param.Name 
      && node.Type.IsSimpleType()) 
     { 
      return Expression.Constant(_value); 
     } 

      return base.VisitParameter(node); 
    } 

    protected override Expression VisitLambda<T>(Expression<T> node) 
    { 
     var parameters = node.Parameters.Where(p => p.Name != _param.Name && p.Type != _param.Type).ToList(); 
     return Expression.Lambda(Visit(node.Body), parameters); 
    } 
} 

Beachten Sie, dass IsSimpleType ist eine Erweiterung ich von this gist von jonothanconway entlehnt.

In meiner Situation wollte ich die Verwendung eines komplexen Typs ersetzen. Beispiel:

Expression<Func<Thing, int, bool>> test = (thing, num) => thing.AnIntProperty == num; 

Also habe ich eine Override der VisitMember-Methode. Dies ist noch eine Arbeit im Gange, aber sieht so aus:

 protected override Expression VisitMember(MemberExpression m) 
    { 
     if (m.Expression != null 
      && m.Expression.NodeType == ExpressionType.Parameter 
      && m.Expression.Type == _param.Type && ((ParameterExpression)m.Expression).Name == _param.Name) 
     { 
      object newVal; 
      if (m.Member is FieldInfo) 
       newVal = ((FieldInfo)m.Member).GetValue(_value); 
      else if (m.Member is PropertyInfo) 
       newVal = ((PropertyInfo)m.Member).GetValue(_value, null); 
      else 
       newVal = null; 
      return Expression.Constant(newVal); 
     } 

     return base.VisitMember(m); 
    } 

Dies wird nur ein Feld oder eine Eigenschaft auflösen. Nächster Schritt könnte sein, Unterstützung für eine Methode hinzuzufügen (aber da sie selbst Parameter haben, so dass noch mehr Arbeit ...)

EDIT: Die oben genannte Member-Besucher-Lösung würde auch nicht die Übergabe eines Objekts selbst unterstützen in einen Methodenaufruf. z.B. (x, thing) => x.DoSomething(thing) so eine Modifikation würde auch dazu benötigt werden.