JavaScript Eval function equivalent in C#

Execute C# code string on the fly

  • Share

Probably who ever was working on some complicated dynamic client-side JavaScript used at least once eval functional in JavaScript. Basically this function executes the code which is passed to it as parameter.

eval("alert('Hello from eval function!')");
    

Fortunately C# is more powerful programming language so therefore it is possible to achieve the same thing, but it is not just one line out of the box as it is in javascript. C# is strongly typed language and therefore certain rules needs to be followed in order to execute some code in runtime.

First thing we need to do is to include few namespaces at the beginning of the code.

using Microsoft.CSharp;
using System;
using System.CodeDom.Compiler;
    
Note

Since we are not using anything that is specific for web, windows, console or any other type of project we do not need to add additional reference in a project.

As we cannot just execute some code, we need to keep it in a strong typed structure, which means inside the class. The whole code along with holder class then needs to be compiled but in a memory, so we do not care about the temp files generated and cleaning up of them.

CSharpCodeProvider codeProvider = new CSharpCodeProvider();
ICodeCompiler codeCompiler = codeProvider.CreateCompiler();
CompilerParameters compileParams = new CompilerParameters();
compileParams.CompilerOptions = "/t:library";
compileParams.GenerateInMemory = true;
    

As soon as we create instance of CSharpCodeProvider and ICodeCompiler implemented class instance, we can compile the code in the memory and access it with reflection. Of course this solution I'm presenting here is not the only one you will find on internet, but the difference is that it handles void and value methods as well as dynamic number of namespaces to be included if you are using some additional reference.

Depending on outType parameter, code will execute void or value method.

public static object Eval(string code, Type outType = null, string[] includeNamespaces = null, string[] includeAssemblies = null)
        {
            StringBuilder namespaces = null;
            object methodResult = null;
            using (CSharpCodeProvider codeProvider = new CSharpCodeProvider())
            {
                ICodeCompiler codeCompiler = codeProvider.CreateCompiler();
                CompilerParameters compileParams = new CompilerParameters();
                compileParams.CompilerOptions = "/t:library";
                compileParams.GenerateInMemory = true;
                if (includeAssemblies != null && includeAssemblies.Any())
                {
                    foreach (string _assembly in includeAssemblies)
                    {
                        compileParams.ReferencedAssemblies.Add(_assembly);
                    }
                }

                if (includeNamespaces != null && includeNamespaces.Any())
                {
                    foreach (string _namespace in includeNamespaces)
                    {
                        namespaces = new StringBuilder();
                        namespaces.Append(string.Format("using {0};\n", _namespace));
                    }
                }
                code = string.Format(
                    @"{1}
                using System;
                namespace CSharpCode{{
                    public class Parser{{
                        public {2} Eval(){{
                            {3} {0};
                        }}
                    }}
                }}",
                    code,
                    namespaces != null ? namespaces.ToString() : null,
                    outType != null ? outType.FullName : "void",
                    outType != null ? "return" : string.Empty
                    );
                CompilerResults compileResult = codeCompiler.CompileAssemblyFromSource(compileParams, code);

                if (compileResult.Errors.Count > 0)
                {
                    throw new Exception(compileResult.Errors[0].ErrorText);
                }
                System.Reflection.Assembly assembly = compileResult.CompiledAssembly;
                object classInstance = assembly.CreateInstance("CSharpCode.Parser");
                Type type = classInstance.GetType();
                MethodInfo methodInfo = type.GetMethod("Eval");
                methodResult = methodInfo.Invoke(classInstance, null);
            }
            return methodResult;
        }
    

Either you need a result of a calculation or showing some message it is not important as long as you invoke method properly like in the next example

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine(CSharpParser.Eval("1+3", typeof(int)));
            CSharpParser.Eval("Console.WriteLine(1+2+3);");
            Console.WriteLine(CSharpParser.Eval(
                "new XElement(\"User\", new XElement(Environment.UserName)).ToString()", 
                typeof(string), 
                new string[]{ "System.Xml.Linq"},
                new string[] { "System.Xml.dll", "System.Xml.Linq.dll" }
                ));
            Console.ReadLine();
        }
    }
    

I adde option for additional assemblies or namespaces you might need to involve in your code string to be executed, so you can even use functions from your custom assemblies by adding your assembly file path in assemblies parameters.

  • Share

Disclaimer

Purpose of the code contained in snippets or available for download in this article is solely for learning and demo purposes. Author will not be held responsible for any failure or damages caused due to any other usage.

Comments for this article

comments powered by Disqus