如何将表达式转换为另一个表达式?

时间:2013-10-03 05:57:24

标签: .net expression currying

我不确定这是否严格 currying,但我基本上想要实现以下目标。给定Expression

Expression<Func<T1, T2, TResult>> expression

我想传入一个参数并生成相应的Expression,其中此参数的值是固定的。结果表达式应该在功能上等同于expression,除了它应该包含少一个参数。

这个结果表达式如下所示:

Expression<Func<T2, TResult>> curriedExpression;

我已经尝试了这个,但它不起作用,因为Expression没有隐式转换为lambda表达式:

curriedExpression = b => expression(fixedValueForT1, b);

请注意,curriedExpression不应包含对expression的调用;它应该包含重复的逻辑,除了固定值。

我希望这是有道理的。如果这个含糊不清或者解释不清楚,请告诉我。

3 个答案:

答案 0 :(得分:3)

我认为你可以通过简单的方式从ExpressionVisitor类派生出来。这是一个概念证明 - 可能过于简单化,但我认为这就是你所追求的:

using System;
using System.Linq.Expressions;

class Test
{
    static void Main()
    {
        Expression<Func<int, int, int>> original = (x, y) => MethodX(x) + MethodY(y);
        Console.WriteLine("Original: {0}", original);
        var partiallyApplied = ApplyPartial(original, 10);
        Console.WriteLine("Partially applied: {0}", partiallyApplied);
    }

    static int MethodX(int x)
    {
        return x + 1;
    }

    static int MethodY(int x)
    {
        return -x;
    }

    static Expression<Func<T2, TResult>> ApplyPartial<T1, T2, TResult>
        (Expression<Func<T1, T2, TResult>> expression, T1 value)
    {
        var parameter = expression.Parameters[0];
        var constant = Expression.Constant(value, parameter.Type);
        var visitor = new ReplacementVisitor(parameter, constant);
        var newBody = visitor.Visit(expression.Body);
        return Expression.Lambda<Func<T2, TResult>>(newBody, expression.Parameters[1]);
    }
}

class ReplacementVisitor : ExpressionVisitor
{
    private readonly Expression original, replacement;

    public ReplacementVisitor(Expression original, Expression replacement)
    {
        this.original = original;
        this.replacement = replacement;
    }

    public override Expression Visit(Expression node)
    {
        return node == original ? replacement : base.Visit(node);
    }
}

输出:

Original: (x, y) => (MethodX(x) + MethodY(y))
Partially applied: y => (MethodX(10) + MethodY(y))

答案 1 :(得分:0)

我刚刚发现使用LinqKit可能是可以的,您可以通过NuGet here获取。{/ p>

我目前没有时间尝试这个例子,但它可能值得研究,所以你不必使用像ExpressionVisitor这样的解决方案。

答案 2 :(得分:0)

这是@ jon-diaet实现的替代方案,具有以下优点/缺点:

优点:

  • 输入表达式可以是任何类型的0..n参数。
  • 您可以通过指定替换参数的索引来解决这些参数中的任何一个。

缺点:

  • 您失去编译时类型安全性(输入表达式没有通用参数,索引可能超出范围且替换为object)。
  • 您必须指定返回的lambda表达式的类型。
private Expression<TLambda> Curry<TLambda>(
    LambdaExpression searchExpression, 
    int replacedParameterIndex, 
    object replacement)
{
    var parameter = searchExpression.Parameters[replacedParameterIndex];
    var constant = Expression.Constant(replacement, parameter.Type);
    var visitor = new ReplacementVisitor(parameter, constant);
    var newBody = visitor.Visit(searchExpression.Body);
    var lambda = Expression.Lambda<TLambda>(newBody, searchExpression.Parameters.Except(new[] { parameter }));

    return lambda;
}

所以在@ jon-skeet的例子中我们会使用:

var partiallyApplied = Curry<int, int>(original, 0, 10);