首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何计算ExpressionVisitor中的表达式?

如何计算ExpressionVisitor中的表达式?
EN

Stack Overflow用户
提问于 2012-01-07 05:18:37
回答 4查看 4.8K关注 0票数 4

在执行表达式之前,我需要使用ExpressionVisitor来分析它。根据我的需要,我需要计算除法表达式的正确部分,但我不知道如何做。下面是我的示例代码:

代码语言:javascript
复制
internal class RulesChecker : ExpressionVisitor
{
    private readonly object data;

    public RulesChecker(object data)
    {
        this.data = data;
    }

    protected override Expression VisitBinary(BinaryExpression node)
    {
        if (node.NodeType == ExpressionType.Divide)
        {
            var rightExpression = node.Right;

            // compile the right expression and get his value            
        }

        return base.VisitBinary(node);
    }
}

假设我有下面的代码要计算:

代码语言:javascript
复制
Expression<Func<DataInfo, decimal?>> expression = x => x.A / (x.B + x.C);
var rulesChecker = new RulesChecker(data);
rulesChecker.Visit(expression);

在VisitBinary函数中,我将接收一个节点,该节点将包含除法运算的左右两部分。我的问题是,我如何评估我将在操作的正确部分获得的值?

EN

回答 4

Stack Overflow用户

发布于 2012-01-10 04:55:29

我认为这个问题最难的部分是处理变量。所以我会从替换常量的变量开始。在此之后,您只需执行并更新表达式。

代码语言:javascript
复制
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace WindowsFormsApplication1
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            var value1 = 1;
            var value2 = 2;
            var value3 = new { MyValue = 3 };
            var data = new DataInfo { A = 10, B = 1, C = -1 };

            Expression<Func<DataInfo, decimal?>> expression = x => x.A / (x.B + x.C) + (value1 + value2) + value3.MyValue;

            // create a list of variables that will be used when evaluating the expression
            var variables = new Dictionary<Type, object>();

            // add the root object
            variables.Add(data.GetType(), data);

            // find variables that are referenced in the expression
            var finder = new VariablesFinder(variables);
            finder.Visit(expression);

            // replace variables with ConstantExpressions
            var visitor = new VariableReplacer(variables);
            var newExpression = visitor.Visit(expression);

            var rulesChecker = new RulesChecker();
            var checkedExpression = rulesChecker.Visit(newExpression);
        }
    }

    internal class RulesChecker : ExpressionVisitor
    {
        protected override Expression VisitBinary(BinaryExpression node)
        {
            if (node.NodeType == ExpressionType.Divide)
            {
                var rightBinaryExpression = node.Right as BinaryExpression;

                if (rightBinaryExpression != null)
                {
                    node = node.Update(node.Left, node.Conversion, this.Execute(rightBinaryExpression));
                }
            }

            return base.VisitBinary(node);
        }

        private Expression Execute(BinaryExpression node)
        {
            var lambda = Expression.Lambda(node);
            dynamic func = lambda.Compile();
            var result = func();

            return Expression.Constant(result, result.GetType());
        }
    }

    internal class VariableReplacer : ExpressionVisitor
    {
        private readonly Dictionary<Type, object> _variables;

        public VariableReplacer(Dictionary<Type, object> variables)
        {
            this._variables = variables;
        }

        protected override Expression VisitMember(MemberExpression node)
        {
            return this.HandleProperty(node) ??
                   this.HandleField(node) ??
                   node;
        }

        private Expression HandleField(MemberExpression memberExpression)
        {
            var fieldInfo = memberExpression.Member as FieldInfo;

            if (fieldInfo != null)
            {
                var value = fieldInfo.GetValue(this.GetVarialbe(fieldInfo));

                return Expression.Constant(value, fieldInfo.FieldType);
            }

            return null;
        }

        private Expression HandleProperty(MemberExpression memberExpression)
        {
            var propertyInfo = memberExpression.Member as PropertyInfo;

            if (propertyInfo != null)
            {
                var value = propertyInfo.GetValue(this.GetVarialbe(propertyInfo), null);

                return Expression.Constant(value, propertyInfo.PropertyType);
            }

            return null;
        }

        private object GetVarialbe(MemberInfo memberInfo)
        {
            return this._variables[memberInfo.DeclaringType];
        }
    }

    internal class VariablesFinder : ExpressionVisitor
    {
        private readonly Dictionary<Type, object> _variables;

        public VariablesFinder(Dictionary<Type, object> variables)
        {
            this._variables = variables;
        }

        protected override Expression VisitConstant(ConstantExpression node)
        {
            this.AddVariable(node.Type, node.Value);

            return base.VisitConstant(node);
        }

        private void AddVariable(Type type, object value)
        {
            if (type.IsPrimitive)
            {
                return;
            }

            if (this._variables.Keys.Contains(type))
            {
                return;
            }

            this._variables.Add(type, value);

            var fields = type.GetFields().Where(x => !x.FieldType.IsPrimitive).ToList();

            foreach (var field in fields)
            {
                this.AddVariable(field.FieldType, field.GetValue(value));
            }
        }
    }

    class DataInfo
    {
        public int A { get; set; }
        public int B { get; set; }
        public int C { get; set; }
        public int D;
    }
}
票数 4
EN

Stack Overflow用户

发布于 2012-01-07 05:34:32

通常,您可以使用此方法来计算lambda表达式(并传递):

代码语言:javascript
复制
protected object EvaluateExpression(Expression expression)
{
    var lambda = Expression.Lambda(expression);

    var compiled = lambda.Compile();

    var value = compiled.DynamicInvoke(null);
    return value;
}

然而,在您的例子中,这是行不通的,因为您要计算的表达式依赖于x,除非您按照Wiktor建议的那样为它指定了一个具体的值,否则就不能对它求值。

为了指定参数的值,您需要修改方法,如下所示:

代码语言:javascript
复制
protected static object EvaluateExpression(Expression expression, ParameterExpression parameterX)
{
    var lambda = Expression.Lambda(expression, parameterX);

    var compiled = lambda.Compile();

    return compiled.DynamicInvoke(5); 
            // 5 here is the actual parameter value. change it to whatever you wish
}

但是,此版本的方法必须将表示表达式中的x的ExpressionParameter对象作为参数,以便它知道如何处理传递给DynamicInvoke()的值。

为了获得适当的ExpressionParameter对象,您需要访问根表达式,而不是它的一个节点,所以我猜在访问器中做这件事会很笨拙。

票数 2
EN

Stack Overflow用户

发布于 2012-01-07 08:12:28

如果我理解正确的话,您希望将访问表达式的结果返回为修改后的表达式树,该树具有以某种方式计算的除法的右侧。您将使用BinaryExpressionUpdate方法将右侧节点替换为您的值:

代码语言:javascript
复制
protected override Expression VisitBinary(BinaryExpression node)
{
    if (node.NodeType == ExpressionType.Divide)
    {
        var rightExpression = node.Right;

        // compile the right expression and get his value            
        var newRightExpression = Evaluate(rightExpression);
        return node.Update(node.Left, node.Conversion, newRightExpression);
    }

    return base.VisitBinary(node);
}

在这段代码中,newRightExpression需要是从Expression继承的类型。如果右侧节点的计算结果为double值,则需要将其包装在ConstantExpression

代码语言:javascript
复制
double rightValue = EvaluateToDouble(rightExpression);
var newRightExpression = Expression.Constant(rightValue, typeof(double));
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/8764662

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档