如何动态评估C#表达式?

时间:2020-03-05 18:50:40  来源:igfitidea点击:

我想做相当于:

object result = Eval("1 + 3");
string now    = Eval("System.DateTime.Now().ToString()") as string

在Biri的链接之后,我得到了以下代码片段(已修改为删除过时的方法ICodeCompiler.CreateCompiler()

private object Eval(string sExpression)
{
    CSharpCodeProvider c = new CSharpCodeProvider();
    CompilerParameters cp = new CompilerParameters();

    cp.ReferencedAssemblies.Add("system.dll");

    cp.CompilerOptions = "/t:library";
    cp.GenerateInMemory = true;

    StringBuilder sb = new StringBuilder("");
    sb.Append("using System;\n");

    sb.Append("namespace CSCodeEvaler{ \n");
    sb.Append("public class CSCodeEvaler{ \n");
    sb.Append("public object EvalCode(){\n");
    sb.Append("return " + sExpression + "; \n");
    sb.Append("} \n");
    sb.Append("} \n");
    sb.Append("}\n");

    CompilerResults cr = c.CompileAssemblyFromSource(cp, sb.ToString());
    if (cr.Errors.Count > 0)
    {
        throw new InvalidExpressionException(
            string.Format("Error ({0}) evaluating: {1}", 
            cr.Errors[0].ErrorText, sExpression));
    }

    System.Reflection.Assembly a = cr.CompiledAssembly;
    object o = a.CreateInstance("CSCodeEvaler.CSCodeEvaler");

    Type t = o.GetType();
    MethodInfo mi = t.GetMethod("EvalCode");

    object s = mi.Invoke(o, null);
    return s;

}

解决方案

回答

通过动态运行编译,如本示例所示。

或者通过使用Flee,这是完全相同的原因。

回答

What are the performance implications of doing this?

我们使用基于上述内容的系统,其中每个Cscript都编译为内存中的程序集,并在单独的AppDomain中执行。还没有缓存系统,因此脚本每次运行时都会重新编译。我已经进行了一些简单的测试,并在我的计算机上大约0.7秒内完成了一个非常简单的" Hello World"脚本的编译,包括从磁盘加载脚本。 0.7秒对于脚本系统来说可以,但是对于响应用户输入可能太慢,在这种情况下,像Flee这样的专用解析器/编译器可能更好。

using System;
public class Test
{
    static public void DoStuff( Scripting.IJob Job)
    {
        Console.WriteLine( "Heps" );
    }
}

回答

看起来还有一种使用RegEx和XPathNavigator评估表达式的方法。我还没有机会对其进行测试,但是我有点喜欢它,因为它不需要在运行时编译代码或者使用无法使用的库。

http://www.webtips.co.in/c/evaluate-function-in-c-net-as-eval-function-in-javascript.aspx

我会尝试一下,然后告诉它是否有效。我也打算在Silverlight中尝试它,但是为时已晚,现在我几乎睡着了。

回答

using System;
using Microsoft.JScript;
using Microsoft.JScript.Vsa;
using Convert = Microsoft.JScript.Convert;

namespace System
{
    public class MathEvaluator : INeedEngine
    {
        private VsaEngine vsaEngine;

        public virtual String Evaluate(string expr)
        {
            var engine = (INeedEngine)this;
            var result = Eval.JScriptEvaluate(expr, engine.GetEngine());

            return Convert.ToString(result, true);
        }

        VsaEngine INeedEngine.GetEngine()
        {
            vsaEngine = vsaEngine ?? VsaEngine.CreateEngineWithType(this.GetType().TypeHandle);
            return vsaEngine;
        }

        void INeedEngine.SetEngine(VsaEngine engine)
        {
            vsaEngine = engine;
        }
    }
}