二进制表达式到 Lambda

发布于 2024-09-15 20:11:58 字数 2379 浏览 3 评论 0原文

这对一些人来说可能很熟悉。我有一个包装类 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

}

但是如何正确横穿树并构建我的 lambda(或委托)

    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 转换为lambda,到目前为止都没有成功。我想要一些建议和指示。请注意,操作的操作数可能是其他表达式对象,并且仅在树的叶子处,它们才会是 ParameterExpressionConstantExpression

谢谢。

This may be familiar to some. I have a wrapper class Ex that wraps an expression tree with a bunch of implicit conversions and operators. Here is simplified version

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

So here is what I want to do:

{ ...
    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

}

But how to I transverse the tree propely and build my lambda's (or delegates)

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

I have tried several things to get to convert the BinaryExpression bex into a lambda, and all have been unsucessful up to now. I'd like some suggestions and pointers. Note that the operands of the operation might be other expression objects and only at the leafs of the tree they will either be ParameterExpression or ConstantExpression.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

夏日浅笑〃 2024-09-22 20:11:58

您可以在调用转换运算符时创建表达式树:

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

在每一步中,您从 Ex 实例中“解压”Expression,应用 Expression.* 方法,并将结果包装在新的 Ex 实例中。

最后,您所要做的就是从最终的Ex 实例中提取 Expression

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;

创建与上面的代码完全相同的表达式树。

You can create the Expression Tree as you call the conversion operators:

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

At each step, you "unpack" the Expression from the Ex instance(s), apply the Expression.* method, and wrap the result in a new Ex instance.

At the end, all you have to do is extract the Expression from the final Ex instance:

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

Note that the C# compiler provides the feature to create an Expression Tree for you:

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

creates exactly the same Expression Tree as the code above.

桜花祭 2024-09-22 20:11:58

如果您的表达式都派生自一个公共类,请在 Gamma 等中查找“Visitor”模式。这甚至是他们使用的例子。

If your expressions all derive from a common class, look up the "Visitor" pattern in Gamma, et al. This is even the example they use.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文