【问题标题】:Evaluating Expression at Runtime在运行时评估表达式
【发布时间】:2011-01-04 19:55:11
【问题描述】:

我有一个 C# 控制台应用程序项目。

我有一个以 nvarchar 形式存储在数据库中的逻辑表达式。

例如,存储的表达式为:((34 > 0) || (US == ES)) && (4312 = 5691)

当我的应用程序运行时,我想检索表达式并对其进行评估,以便结果为真或假。

我怎样才能在运行时做到这一点?

【问题讨论】:

  • this 问题中的答案可能会有用。
  • +1 for NCalc : 我在多个项目中使用它,效果很好

标签: c# .net parsing evaluation


【解决方案1】:

这是一个相当不寻常的解决方案,涉及 JScript:

  • 使用以下代码创建一个 JScript 类:

    public class JsMath {
        public static function Eval(expression:String) : Object {
            return eval(expression);
        }
    }
    
  • 编译成DLL:

    jsc /target:library /out:JsMath.dll JsMath.js
    
  • 在您的 C# 项目中,引用 JsMath.dll 和 Microsoft.JScript.dll

  • 现在你可以使用Eval方法如下:

    string expression = "((34 > 0) || ('US' == 'ES')) && (4312 == 5691)";
    bool result = (bool)JsMath.Eval(expression);
    

好处:

  • 解析表达式无需任何工作,JScript 引擎会为您完成这项工作
  • 无需编译任意代码(如果代码由用户输入,这可能是一个很大的安全漏洞)
  • 应该使用任何简单的数学或逻辑表达式,只要它遵循 JScript 语法

缺点:

  • 没有办法传递变量(据我所知)
  • 需要对 JScript 程序集的引用(在大多数情况下这不是大问题,但我不确定此程序集在客户端配置文件或 Silverlight 中是否可用)

【讨论】:

  • +1 我自己已经成功使用过这种技术。当然是一种鲜为人知的技术。
  • 尚未使用它,但如果您想评估不受信任的字符串,您可能需要采取额外的预防措施来沙箱您的 javascript。
  • “很容易在输入字符串中检测到它”我对此感觉不太好。在大多数动态类型语言中,绕过这样的黑名单很容易,例如使用 eval。诀窍是以不作为字符串文字出现的形式创建邪恶字符串,然后使用某些语言功能访问具有该名称的变量/类/...。
  • 其实,你仍然可以做一些奇怪的事情,比如JsMath.Eval("JsMath.Eval(expression)")(可以预见会抛出异常“Out of stack space”)
  • @CodeInChaos:你是对的......但你也可以禁止在表达式中使用eval ;)。无论如何,您确实需要对您接受的输入格外小心......
【解决方案2】:

您可以将表达式解析为 .NET Expression 类并编译并运行它以获得结果。

该类已经支持您在示例中的所有逻辑操作,尽管它看起来不明确(您以非常相似的方式同时使用===)。

不过,您必须编写自己的解析器/转换器。

【讨论】:

    【解决方案3】:

    我从这里 (https://odetocode.com/articles/80.aspx) 编写了 K. Scott Allen 的 JScript 内联 Eval 调用程序的更紧凑和高效的版本:

    using System;
    using System.CodeDom.Compiler;
    using Microsoft.JScript;
    
    class JS
    {
        private delegate object EvalDelegate(String expr);
        private static EvalDelegate moEvalDelegate = null;
    
        public static object Eval(string expr)
        {
            return moEvalDelegate(expr);
        }
    
        public static T Eval<T>(string expr)
        {
            return (T)Eval(expr);
        }
    
        public static void Prepare()
        {
        }
    
        static JS()
        {
            const string csJScriptSource = @"package _{ class _{ static function __(e) : Object { return eval(e); }}}";
            var loParameters = new CompilerParameters() { GenerateInMemory = true };
            var loMethod = (new JScriptCodeProvider()).CompileAssemblyFromSource(loParameters, csJScriptSource).CompiledAssembly.GetType("_._").GetMethod("__");
            moEvalDelegate = (EvalDelegate)Delegate.CreateDelegate(typeof(EvalDelegate), loMethod);
        }
    }
    

    像这样使用它:

    JS.Eval<Double>("1 + 4 + 5 / 99");
    

    返回:

    5.05050505050505

    如果你愿意,你也可以扩展它来传递变量值,例如传入名称和值的字典。静态类的第一次使用需要 100-200 毫秒,之后它几乎是瞬时的并且不需要单独的 DLL。如果需要,调用 JS.Prepare() 进行预编译以停止初始延迟。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-06
      • 2022-10-15
      相关资源
      最近更新 更多