2013-07-31 6 views
8

Jeśli mam wyraz delegat funkcji, które ma szereg parametrów tak:Zmniejszyć wyrażenie przez wprowadzenie parametru

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

czy istnieje sposób/jak mogę zastąpić jedną z wartości (słownie 5 dla num1) i uzyskać równoważne wyrażenie:

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

EDIT:

musi również rozwiązać złożonych typów, np:

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

możliwy duplikat [C# Linq vs. Currying] (http://stackoverflow.com/questions/8826266/c-sharp-linq-vs-currying) –

+2

podczas gdy głosowałem, że to dup, to pytanie może być w rzeczywistości inne . Może szukasz sposobu użycia gościa ekspresyjnego do zastąpienia argumentu wartością? (coś podobnego do [tego] (http://stackoverflow.com/questions/11164009/using-a-linq-expressionvisitor-to-replace-primitive-parameters-with-property-ref)) –

+0

Nie całkiem. Chcę pracować z wyrażeniami. Myślę, że jest bliższa [this] (http://stackoverflow.com/questions/11159697/replace-parameter-in-lambda-expression) lub [this] (http://stackoverflow.com/questions/5631070/currying -expressions-in-c-sharp) –

Odpowiedz

2

Moja odpowiedź brzmiała, jak używać gościa z ekspresji. (dzięki @ Alexei-levenkov za wskazanie tego).

Odpowiedź dla mojej konkretnej sytuacji była trochę inna niż dla uproszczonego przykładu, którego użyłem w pytaniu. Ale pod względem kompletności, tutaj był, jak to zrobiłem:

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); 
    } 
} 

Zauważ, że IsSimpleType jest rozszerzeniem pożyczyłem od this gist przez jonothanconway.

W mojej sytuacji chciałem zastąpić użycie złożonego typu. np .:

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

Więc zrobiłem nadpisanie metody VisitMember. To wciąż trwa, ale wygląda następująco:

 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); 
    } 

To rozwiąże tylko pole lub właściwość. Następnym krokiem może być dodanie obsługi metody (ale ponieważ mają same parametry, więc zajmie to więcej pracy ...)

EDYCJA: Powyższe rozwiązanie dla odwiedzających użytkowników nie obsługuje również przekazywania samego obiektu do wywołania metody. na przykład (x, thing) => x.DoSomething(thing), więc modyfikacja byłaby potrzebna, aby to zrobić.