Бинарное выражение в лямбду - PullRequest
4 голосов
/ 20 августа 2010

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

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

}

Но как мне аккуратно пересечь дерево и построить лямбду (или делегаты)

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

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

Спасибо.

Ответы [ 2 ]

6 голосов
/ 21 августа 2010

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

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 голосов
/ 21 августа 2010

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

...