2013-10-03 3 views
1

Я не уверен, что это строго currying, но я в основном хочу добиться следующего. Учитывая Expression:Как я могу выразить выражение в другое выражение?

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

Я хочу передать в один аргументов и получением соответствующего Expression где значение этого параметра фиксировано. Полученное выражение должно быть функционально эквивалентно expression, за исключением того, что оно должно содержать один меньший параметр.

Это Результирующее выражение будет выглядеть примерно так:

Expression<Func<T2, TResult>> curriedExpression; 

Я попытался это, но он не работает, потому что Expression не неявно преобразовать в лямбда-выражения:

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

Обратите внимание, что curriedExpression не должен содержать звонок expression; он должен содержать дублированную логику, за исключением фиксированного значения.

Надеюсь, это имеет смысл. Дайте мне знать, если это неоднозначно или не объяснено хорошо.

ответ

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)) 
+0

Спасибо. Есть ли более простой способ, который ближе к тому, как вы будете делать то же самое с 'Func'? – Sam

+0

Я боюсь, что нет более простого способа, когда вы работаете с выражениями, вы должны иметь дело с синтаксической структурой, и это всегда довольно сложный бизнес! –

0

Я только что обнаружил, что это, вероятно, можно с помощью LinqKit , которую вы можете получить через NuGet here.

У меня нет времени на то, чтобы опробовать этот пример, но, вероятно, стоит изучить его, поэтому вам не нужно использовать такое решение, как ExpressionVisitor.

0

Это является альтернативой @ джон-тарелочкам реализации, со следующими плюсы/минусы:

За:

  • Выражение вход может иметь 0..n аргументы любого типа.
  • Вы можете выполнить любой из этих параметров, указав индекс замененного.

Минусы:

  • Вы теряете время компиляции безопасности типа (выражение ввода не имеет общие параметры, индекс может быть вне диапазона и замена является object).
  • Необходимо указать тип возвращаемого лямбда-выражения.
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; 
} 

Таким образом, в примере @ джон-тарелочкам мы будем использовать:

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

 Смежные вопросы

  • Нет связанных вопросов^_^