2013-07-31 103 views
8

減少的表達。如果我有一個函數委託,它採用像這樣的一些參數的表達式:通過輸入參數

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

是有辦法/ I如何可以替換值中的一個(比方說5 num1),並獲得相當的表達:

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

編輯:

還需要解決複雜的類型,例如:

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

[C#Linq vs. Currying]可能重複(http://stackoverflow.com/questions/8826266/c-sharp-linq-vs-currying) –

+2

雖然我投票是dup,但這個問題實際上可能是不同的。也許你正在尋找表達式訪問者的用法來替代有價值的參數? (沿着[this]的行)(http://stackoverflow.com/questions/11164009/using-a-linq-expressionvisitor-to-replace-primitive-parameters-with-property-ref)) –

+0

不完全。我想與表達式一起工作。我認爲它更接近[this](http://stackoverflow.com/questions/11159697/replace-parameter-in-lambda-expression)或[this](http://stackoverflow.com/questions/5631070/currying -expressions-在-C-尖銳) –

回答

2

我的答案是使用表達式訪問者。 (感謝@ Alexei-levenkov指出)。

對於我的特定情況,答案與我在問題中使用的簡化示例稍有不同。但是,爲了完整性,這裏是我是如何做的:

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

注意IsSimpleType是一個擴展,我從this gist藉由jonothanconway。

在我的情況下,我想替換使用複雜類型。例如:

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

所以我重寫了VisitMember方法。這仍然是一個進展中的工作,但看起來是這樣的:

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

這隻能解決一個字段或屬性。下一步可能是添加對方法的支持(但是因爲它們自身有參數,所以需要更多的工作......)

編輯:上述成員訪問者解決方案也不支持傳遞對象本身進入方法調用。例如(x, thing) => x.DoSomething(thing)所以需要修改以做到這一點。