views:

2799

answers:

6

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;

}
+12  A: 

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

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

Biri
+3  A: 

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.

JJJ
A: 

What are the performance implications of doing this?

Terrapin
That wasn't my downvote, but if you mention performance, my question in return is how often do you do it?
Mike Dunlavey
+1  A: 

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" );
    }
}
JJJ
A: 

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.

+1  A: 
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;
        }
    }
}
Rudolf_Abel