C# 计算数学表达式的最佳和最短方法

C# 计算数学表达式的最佳和最短方法,c#,.net,expression,evaluation,C#,.net,Expression,Evaluation,计算表达式的算法有很多,例如: 是否有任何方法可以使用C#.net反射或其他现代.net技术计算任何数学表达式 这当然是可能的。这个班基本上就是这么做的。 我为您编写了一些示例使用代码。您需要包括以下名称空间: System.CodeDom.Compiler System.CodeDom 微软.CSharp 制度反思 代码如下: string source = @" class MyType { public static int Evaluate(<!parameter

计算表达式的算法有很多,例如:


  • 是否有任何方法可以使用C#.net反射或其他现代.net技术计算任何数学表达式

    这当然是可能的。这个班基本上就是这么做的。 我为您编写了一些示例使用代码。您需要包括以下名称空间:

    • System.CodeDom.Compiler
    • System.CodeDom
    • 微软.CSharp
    • 制度反思
    代码如下:

    string source = @"
    class MyType
    {
        public static int Evaluate(<!parameters!>)
        {
            return <!expression!>;
        }
    }
    ";
    
    string parameters = "int a, int b, int c";
    string expression = "a + b * c";
    
    string finalSource = source.Replace("<!parameters!>", parameters).Replace("<!expression!>", expression);
    
    CodeSnippetCompileUnit compileUnit = new CodeSnippetCompileUnit(finalSource);
    CodeDomProvider provider = new CSharpCodeProvider();
    
    CompilerParameters parameters = new CompilerParameters();
    
    CompilerResults results = provider.CompileAssemblyFromDom(parameters, compileUnit);
    
    Type type = results.CompiledAssembly.GetType("MyType");
    MethodInfo method = type.GetMethod("Evaluate");
    
    // The first parameter is the instance to invoke the method on. Because our Evaluate method is static, we pass null.
    int result = (int)method.Invoke(null, new object[] { 4, -3, 2 });
    
    stringsource=@”
    类MyType
    {
    公共静态int Evaluate()
    {
    返回;
    }
    }
    ";
    字符串参数=“inta,intb,intc”;
    字符串表达式=“a+b*c”;
    字符串finalSource=source.Replace(“,参数).Replace(“,表达式);
    CodeSnippetCompileUnit compileUnit=新的CodeSnippetCompileUnit(finalSource);
    CodeDomProvider provider=新的CSharpCodeProvider();
    CompilerParameters参数=新的CompilerParameters();
    CompilerResults results=provider.compileasemblyFromdom(参数,compileUnit);
    Type Type=results.CompiledAssembly.GetType(“MyType”);
    MethodInfo方法=type.GetMethod(“评估”);
    //第一个参数是调用方法的实例。因为我们的求值方法是静态的,所以我们传递null。
    int result=(int)method.Invoke(null,新对象[]{4,-3,2});
    
    用任何东西替换“参数”和“表达式”,您就拥有了一个通用表达式计算器

    如果在results.CompiledAssembly中获得FileNotFoundException,则代码段无法编译


    您可能还想看看System.CodeDom.CodeSnippetExpression类。它用于更具体地读取表达式,但表达式本身无法编译,因此需要使用更多的CodeDom来围绕它构建工作类和方法。如果您希望能够以编程方式操纵正在生成的类的类型,那么这非常有用。CodeSnippetCompileUnit可以一次生成整个工作类(例如更简单),但要操作它,必须进行不方便的字符串操作。

    虽然使用编译器服务是一种简单而有效的解决方案,但如果用户输入表达式,则会引发严重的安全问题,因为它几乎可以执行任何事情

    还有另一个非常简单的解决方案更安全:利用JScript
    Eval
    函数。您只需遵循以下步骤:

    创建名为JsMath.js的js文件:

    class JsMath
    {
        static function Eval(expression : String) : double
        {
            return eval(expression);
        };
    }
    
    将其编译到类库中:

    jsc /t:library JsMath.js
    
    在C#项目中引用JsMath库,并像这样使用它:

    double result = JsMath.Eval(expression);
    

    对我来说,Vici.Parser工作得非常好:,它是迄今为止我发现的最灵活的表达式解析器

    (我们使用它来建立“人类可读”的业务规则,数据由SQL server数据库提供)


    示例是可用的,开发人员提供了很好的支持(请查看网站论坛)。

    进一步了解Thomas的答案,实际上可以直接从C#访问(不推荐使用的)JScript库,这意味着您可以使用JScript的
    eval
    功能

    using Microsoft.JScript;        // needs a reference to Microsoft.JScript.dll
    using Microsoft.JScript.Vsa;    // needs a reference to Microsoft.Vsa.dll
    
    // ...
    
    string expr = "7 + (5 * 4)";
    Console.WriteLine(JScriptEval(expr));    // displays 27
    
    // ...
    
    public static double JScriptEval(string expr)
    {
        // error checking etc removed for brevity
        return double.Parse(Eval.JScriptEvaluate(expr, _engine).ToString());
    }
    
    private static readonly VsaEngine _engine = VsaEngine.CreateEngine();
    
    这是最好的。您也可以在nugget中找到它。

    NCalc是.NET中的数学表达式计算器。NCalc可以解析任何表达式并计算结果,包括静态或动态参数和自定义函数

    我认为这是最好的方法。这太神奇了。 使用DataColumn对象的“expression”参数可以非常轻松地解决以下主题:

    static double Evaluate(string expression)
    {
        var loDataTable = new DataTable();
        var loDataColumn = new DataColumn("Eval", typeof(double), expression);
        loDataTable.Columns.Add(loDataColumn);
        loDataTable.Rows.Add(0);
        return (double)(loDataTable.Rows[0]["Eval"]);
    }
    
    您可以使用实现调车场算法的库,我是该库的作者。它支持简单表达式,如
    2.5+5.9
    17.89-2.47+7.16
    5/2/2+1.5*3+4.58
    ,带括号的表达式
    ((9-6/2)*2-4)/2-6-1/(2+24/(2+4))
    ,以及带变量的表达式:

    var a = 6;
    var b = 4.32m;
    var c = 24.15m;
    var engine = new ExpressionEvaluator();
    engine.Evaluate("(((9-a/2)*2-b)/2-a-1)/(2+c/(2+4))", new { a, b, c});
    
    dynamic dynamicEngine = new ExpressionEvaluator();
    
    var a = 6;
    var b = 4.5m;
    var c = 2.6m;
    
    dynamicEngine.Evaluate("(c+b)*a", a: 6, b: 4.5, c: 2.6);
    
    还可以将参数作为命名变量传递:

    var a = 6;
    var b = 4.32m;
    var c = 24.15m;
    var engine = new ExpressionEvaluator();
    engine.Evaluate("(((9-a/2)*2-b)/2-a-1)/(2+c/(2+4))", new { a, b, c});
    
    dynamic dynamicEngine = new ExpressionEvaluator();
    
    var a = 6;
    var b = 4.5m;
    var c = 2.6m;
    
    dynamicEngine.Evaluate("(c+b)*a", a: 6, b: 4.5, c: 2.6);
    

    它支持.Net标准2.0,因此可以从.Net Core和.Net完整框架项目中使用,并且它没有任何外部依赖项。

    使用新的Roslyn API动态编译代码,并在.Net Core项目中加载程序集

    string finalSource=。。。;
    IEnumerable引用=。。。;
    var compilation=csharpcomilation.Create(“动态”,
    新[]{
    SyntaxFactory.ParseSyntaxTree(
    最终来源:,
    CSharpParseOptions.Default
    .WithLanguageVersion(LanguageVersion.Latest)
    ) },
    references.Select(a=>MetadataReference.CreateFromFile(a.Location)),
    新的CSharpCompliationOptions(OutputKind.DynamicallyLinkedLibrary)
    .WithAssemblyIdentityComparer(桌面AssemblyIdentityComparer.Default)
    );
    使用var ms=new MemoryStream();
    var e=compilation.Emit(ms);
    如果(!e.成功)
    抛出新异常(“编译失败”);
    Seek女士(0,SeekOrigin.Begin);
    var context=newassemblyLoadContext(null,true);
    var assembly=context.LoadFromStream(ms);
    
    请注意,以及您正在编译的源所需的任何其他类型。为了在同一进程中加载已编译的程序集,引用将需要包括:

    AppDomain.CurrentDomain.GetAssemblys()。其中(a=>a.GetName().Name==“netstandard”).Single(),
    类型(对象)。组件
    
    不久前我问了一个类似的问题。您可能想看看其中的一些答案:您是否找到了一种链接到其余“静态/预编译”代码中使用的变量的方法?实际上可以直接从C#访问
    eval
    函数,而无需中间的JScript编译步骤。有关详细信息,请参阅我的答案。为了避免使用编译器服务出现安全问题,我使用ANTL预解析用户表达式并避免任何奇怪的输入。如果您正在寻找性能,
    eval()
    函数可能会