C# 为简单的数学公式创建表达式

C# 为简单的数学公式创建表达式,c#,.net,expression,.net-4.5,expression-trees,C#,.net,Expression,.net 4.5,Expression Trees,我对表达式很感兴趣,出现了一个问题:它抛出了一个我没有想到的异常 我有一个输入-简单的数学公式,例如2*x+3,我想为它创建一个表达式树。所以我写了这个代码 using System; using System.Linq.Expressions; namespace ConsoleApplication50 { class Program { static void Main() { string s = "3*x^2+2

我对表达式很感兴趣,出现了一个问题:它抛出了一个我没有想到的异常

我有一个输入-简单的数学公式,例如
2*x+3
,我想为它创建一个表达式树。所以我写了这个代码

using System;
using System.Linq.Expressions;

namespace ConsoleApplication50
{
    class Program
    {
        static void Main()
        {
            string s = "3*x^2+2/5*x+4";
            Expression<Func<double, double>> expr = MathExpressionGenerator.GetExpression(s);
            Console.WriteLine(expr);

            var del = expr.Compile();

            Console.WriteLine(del(10));
        }


    }

    static class MathExpressionGenerator
    {
        public const string SupportedOps = "+-*/^";
        private static readonly ParameterExpression Parameter = Expression.Parameter(typeof(double), "x");

        public static Expression<Func<double, double>> GetExpression(string s)
        {
            ParameterExpression parameterExpression = Expression.Parameter(typeof(double), "x");
            Expression result = GetExpressionInternal(s);
            return Expression.Lambda<Func<double, double>>(result, parameterExpression);
        }

        private static Expression GetExpressionInternal(string s)
        {
            double constant;
            if (s == "x")
                return Parameter;
            if (double.TryParse(s, out constant))
                return Expression.Constant(constant, typeof(double));
            foreach (char op in SupportedOps)
            {
                var split = s.Split(new[] { op }, StringSplitOptions.RemoveEmptyEntries);
                if (split.Length > 1)
                {
                    var expression = GetExpressionInternal(split[0]);
                    for (int i = 1; i < split.Length; i++)
                    {
                        expression = RunOp(expression, GetExpressionInternal(split[i]), op);
                    }
                    return expression;
                }
            }
            throw new NotImplementedException("never throws");
        }

        private static Expression RunOp(Expression a, Expression b, char op)
        {
            switch (op)
            {
                case '+':
                    return Expression.Add(a, b);
                case '-':
                    return Expression.Subtract(a, b);
                case '/':
                    return Expression.Divide(a, b);
                case '*':
                    return Expression.Multiply(a, b);
                case '^':
                    return Expression.Power(a, b);
            }
            throw new NotSupportedException();
        }
    }
}
使用系统;
使用System.Linq.Expressions;
命名空间控制台应用程序50
{
班级计划
{
静态void Main()
{
字符串s=“3*x^2+2/5*x+4”;
表达式expr=MathExpressionGenerator.GetExpression;
控制台写入线(expr);
var del=expr.Compile();
控制台写入线(del(10));
}
}
静态类MathExpressionGenerator
{
public const string SupportedOps=“+-*/^”;
私有静态只读参数Expression Parameter=Expression.Parameter(typeof(double),“x”);
公共静态表达式GetExpression(字符串s)
{
ParameterExpression ParameterExpression=Expression.Parameter(typeof(double),“x”);
表达式结果=GetExpressionInternal;
返回表达式.Lambda(结果,参数表达式);
}
私有静态表达式GetExpressionInternal(字符串s)
{
双常数;
如果(s==“x”)
返回参数;
if(双锥巴色(s,输出常数))
返回表达式.Constant(Constant,typeof(double));
foreach(SupportedOps中的字符操作)
{
var split=s.split(新[]{op},StringSplitOptions.RemoveEmptyEntries);
如果(拆分长度>1)
{
var expression=GetExpressionInternal(拆分[0]);
for(int i=1;i
但我有一个错误:

未处理的异常:System.InvalidOperationException:的变量“x” 类型“System.Double”从作用域“”引用,但未定义 在 System.Linq.Expressions.Compiler.VariableBinder.Reference(ParameterExpress 离子节点,VariableStorageKind存储)位于 System.Linq.Expressions.Compiler.VariableBinder.VisitParameter(ParameterEx 压力节点)在 System.Linq.Expressions.ParameterExpression.Accept(ExpressionVisitor 访问或)在。。。等等


请告诉我,这是怎么解决的?这里我有一个全局参数并引用它,所以我不知道为什么它会这样说。

代码的问题是,您有两个
x
参数的实例。其中一个是跨表达式生成使用的私有静态字段,第二个是在Lambda创建中创建和使用的字段

如果您有ParameterExpression,那么您应该在表达式中使用相同的实例,并将相同的实例传递到Lambda生成中,否则它将像您的示例中那样失败

如果您删除
参数expression
,并使用私有
参数
字段,则可以正常工作,如下所示:

public static Expression<Func<double, double>> GetExpression(string s)
{
    Expression result = GetExpressionInternal(s);
    return Expression.Lambda<Func<double, double>>(result, Parameter);
}
公共静态表达式GetExpression(字符串s)
{
表达式结果=GetExpressionInternal;
返回表达式.Lambda(结果,参数);
}

在.NetFiddle中的工作示例-

该死的复制粘贴,我没有引用任何对象。这显然是这种行为的原因。谢谢你help@AlexZhukovskiy:我正在使用您的代码,它使用简单的公式非常有效,但如何扩展代码以支持括号:
()
e
Math.Expr
。我试过了,但只得到了一点exceptions@PaulMeems要使它与括号一起工作并不容易,因为它们正在改变优先级,而我只是用
string.Split进行除法,它忽略了它。你基本上必须从头重写它。你可以先尝试打开方括号(从3*(x+2)转换到3*x+3*2,然后运行这个家伙。这可能比编写严肃的解析器更容易。
static void Main(string[] args)
{
        var str = @"3*x^2+2/5*x+4";
        str = Transform(str);
        var param = Expression.Parameter(typeof (double), "x");
        var expression = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] {param}, null, str);
        var exp10 = expression.Compile().DynamicInvoke(10);
        Console.WriteLine(exp10);
}
    public const string SupportedOps = "+-*/";//separators without ^
    private static string Transform(string expression)
    {
        //replace x^y with Math.Pow(x,y)
        var toBeReplaced = expression.Split(SupportedOps.ToCharArray()).Where(s => s.Contains("^"));
        var result = expression;
        return toBeReplaced.Aggregate(expression, (current, str) => current.Replace(str, string.Format("Math.Pow({0})", str.Replace('^', ','))));
        //OR
        //foreach (var str in toBeReplaced)
        //{
        //    result =result.Replace(str, string.Format("Math.Pow({0})", str.Replace('^', ',')));
        //}
        //return result;    
}