Friday, February 4, 2011

How can I evaluate a C# expression dynamically?

I would like to do the equivalent of:

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

Following Biri s link, I got this snippet (modified to remove obsolete method 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;

}
  • By using compile on the fly, like this example shows.

    Or by using Flee, which is for exactly the same reason.

    From Biri
  • Remember that unless you do your compilation and execution in a seperate AppDomain, you may run into memory problems. Assemblies generated in this manner cannot be unloaded, but if you create the assembly in a seperate AppDomain you can unload the AppDomain and thereby unload the generated assembly.

    From JJJ
  • What are the performance implications of doing this?

    Mike Dunlavey : That wasn't my downvote, but if you mention performance, my question in return is how often do you do it?
    From Terrapin
  • What are the performance implications of doing this?

    We use a system based on something like the above mentioned, where each C# script is compiled to an in-memory assembly and executed in a separate AppDomain. There's no caching system yet, so they scripts are recompiled every time they run. I've done some simple testing and a very simple "Hello World" script compiles in about 0.7 seconds on my machine, including loading the script from disk. 0.7 seconds is fine for a scripting system, but might be too slow for responding to user input, in that case a dedicated parser/compiler like Flee might be better.

    using System;
    public class Test
    {
        static public void DoStuff( Scripting.IJob Job)
        {
         Console.WriteLine( "Heps" );
        }
    }
    
    From JJJ
  • Looks like there is also a way of doing it using RegEx and XPathNavigator to evaluate the expression. I did not have the chance to test it yet but I kind of liked it because it did not require to compile code at runtime or use libraries that could not be available.

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

    I'll try it and tell later if it worked. I also intend to try it in Silverlight, but it is too late and I'm almost asleep to do it now.

  • 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;
            }
        }
    }
    

0 comments:

Post a Comment