2010-08-20 2 views
4

Это может быть знакомо некоторым. У меня есть класс-оболочка Ex, который обертывает дерево выражений кучей неявных преобразований и операторов. Вот упрощенная версияBinaryExpression to Lambda

public class Ex 
{ 
    Expression expr; 

    public Ex(Expression expr) 
    { 
     this.expr = expr; 
    } 
    public static implicit operator Expression(Ex rhs) { return rhs.expr; } 
    public static implicit operator Ex(double value) 
    { return new Ex(Expression.Constant(value, typeof(double))); } 
    public static implicit operator Ex(string x) 
    { return new Ex(Expression.Parameter(typeof(double), x)); } 
    public static Ex operator +(Ex left, Ex right) 
    { 
     return new Ex(Expression.Add(left, right)); 
    } 
    public static Ex operator -(Ex rhs) 
    { 
     return new Ex(Expression.Negate(rhs)); 
    } 
    public static Ex operator -(Ex left, Ex right) 
    { 
     return new Ex(Expression.Subtract(left, right)); 
    } 
    public static Ex operator *(Ex left, Ex right) 
    { 
     return new Ex(Expression.Multiply(left, right)); 
    } 
    public static Ex operator /(Ex left, Ex right) 
    { 
     return new Ex(Expression.Divide(left, right)); 
    } 
} 

Так вот что я хочу сделать:

{ ... 
    Ex x = "x"; 
    Ex y = 10.0; 
    Ex z = x + y; 

    LambdaExpression lambda = BuildLambda(z); 
    Func<double,double> f = (Func<double,double>)lambda.Compile(); 

    // f(5) = 15 

} 

Но как я пробежать дерево propely и построить мой лямбда (или делегатов)

LambdaExpression BuildLambda(Expression e) 
    { 
     ConstantExpression cex = e as ConstantExpression; 
     if(cex != null) 
     { 
      return Expression.Lambda<Func<double>>(cex); 
     } 
     ParameterExpression pex = e as ParameterExpression; 
     if (pex != null) 
     { 
      Func<Expression, Expression> f = (x) => x; 
      Expression body = f(pex); 
      return Expression.Lambda<Func<double, double>>(body , pex); 
     } 
     BinaryExpression bex = e as BinaryExpression; 
     if (bex != null) 
     { 
      LambdaExpression left = GetLambda(bex.Left); 
      LambdaExpression rght = GetLambda(bex.Right); 
    // Now what? 
     } 
     return null; 
    } 

Я попробовал несколько вещей, чтобы преобразовать bex BinaryExpression в лямбда, и до сих пор все были неаккуратными. Я бы хотел несколько советов и указаний. Обратите внимание, что операнды операции могут быть другими объектами выражения, и только у листьев дерева они будут либо ParameterExpression, либо ConstantExpression.

Спасибо.

ответ

6

Вы можете создать дерево выражений, как вы называете операторы преобразования:

public class Ex 
{ 
    private readonly Expression expr; 

    public Ex(Expression expr) 
    { 
     this.expr= expr; 
    } 

    public Expression Expression 
    { 
     get { return this.expr; } 
    } 

    public static Ex operator +(Ex left, Ex right) 
    { 
     return new Ex(Expression.Add(left.expr, right.expr)); 
    }          ↑   ↑ 

    // etc. 
} 

На каждом шаге вы «распаковать» в Expression из Ex экземпляра (ов), применять метод Expression.* и оберните приводят к новому экземпляру Ex.

В конце концов, все, что вам нужно сделать, это извлечь Expression из конечного Ex например:

Ex x = new Ex(Expression.Parameter(typeof(double), "x")); 
Ex y = new Ex(Expression.Constant(10.0, typeof(double))); 
Ex z = x + y; 

Expression<Func<double, double>> result = 
    Expression.Lambda<Func<double, double>>(z.Expression, x.Expression); 

Обратите внимание, что C# компилятор предоставляет возможность для создания дерева выражений для вас:

Expression<Func<double, double>> result = x => x + 10.0; 

создает точно такое же дерево выражений, как и код выше.

+0

Создание деревьев - не проблема. Мой 'Ex x =" x "' отлично работает. Создание делегатов было решено с помощью «Ex x =« x »; Ex y = 10,0; Ex z = x + y; Выражение > lambda = Expression.Lambda > ((выражение) z, (ParameterExpression) (выражение) x); Func f3 = (Func ) lambda.Compile(); двойной результат = f3 (5); // result = 15' Большое спасибо. Хитрость заключается в том, чтобы просто сделать это и дать компилятору выяснить детали. – ja72

+0

Есть ли у вас справочник/пример/учебник, посвященный особенностям построения деревьев выражений из кода, а затем компиляция в делегаты? Следующий шаг - расширить это на вызовы метода/функции в дополнение к простой арифметике, сделанной выше. – ja72

+0

@ jalexiou: Я думаю, что ваш класс Ex может быть лишним - вы, похоже, копируете много функций, которые уже есть. Если вы хотите динамически определять деревья выражений, вы можете использовать методы Expression. *. Если вы хотите статически определять деревья выражений, компилятор C# может генерировать их для вас. Если все, что вам нужно, это статическое выражение лямбда, вам вообще не нужны деревья выражений. Что ты пытаешься сделать? – dtb

0

Если ваши выражения все вытекают из общего класса, посмотрите шаблон «Посетитель» в Gamma и др. Это даже пример, который они используют.

+0

'System.Linq.Expressions.ExpressionVisitor' - это внутренний класс, и я не могу наследовать его. – ja72

+2

@ jalexiou: System.Linq.Expressions.ExpressionVisitor был опубликован в .NET 4.0. http://msdn.microsoft.com/en-us/library/system.linq.expressions.expressionvisitor.aspx – dtb