Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I use the CSharpCodeProvider to execute C# code from a string. But of course it creates a class for the code to run. Is there a way to call a method from the current app it is called from? For eg to call CallTest() from a parsed script in this (pseudo) example code

public class Test {
// lot of code
private void CallTest() {
    Console.WriteLine("CallTest()");
public object OnMethodInvoke(string functionName, List<object> parameters) {
    // lot of if statements
    if(functionName == "exec") {
        CSharpCodeProvider c = new CSharpCodeProvider();
        ICodeCompiler icc = c.CreateCompiler();
        CompilerParameters cp = new CompilerParameters();
        cp.ReferencedAssemblies.Add("System.dll");
        cp.CompilerOptions = "/t:library";
        cp.GenerateInMemory = true;
        System.Text.StringBuilder sb = new System.Text.StringBuilder("");
        sb.Append("using System;\n");
        sb.Append("namespace ScriptStack.Dynamic {\n");
        sb.Append("public class Code {\n");
        sb.Append("public object EvalCode(){\n");
        sb.Append((string)parameters[0]);
        sb.Append("}\n");
        sb.Append("}\n");
        sb.Append("}\n");
        CompilerResults cr = icc.CompileAssemblyFromSource(cp, sb.ToString());
        if (cr.Errors.Count > 0) return null;
        System.Reflection.Assembly a = cr.CompiledAssembly;
        object o = a.CreateInstance("ScriptStack.Dynamic.Code");
        MethodInfo mi = o.GetType().GetMethod("EvalCode");
        return mi.Invoke(o, null);
                Based on your problem statement, it seems what you are trying to achieve is easier to do with Reflection. Is there a reason you prefer CodeDom?
– timur
                Feb 8, 2020 at 12:05

If I got your problem statement correctly, you want to be able to compile a statement like

t.CallTest(); //where t is an instance of your class

and run it.

As I hinted in the comments, Reflection might be way easier to do. But for a second, let's imagine you need to keep pressing on and generate some fancy code that you will then need to run (which is where LINQ Expression trees might be a better answer, but never mind - suppose we're still sticking to CodeDom).

There are a couple of issues I see to overcome here:

  • The unit of compilation in CodeDom is assembly, I don't believe you can have assembly nested in a class. Therefore you need to define at least a namespace and a class (which you seem to have done already)
  • I also don't believe CodeDom allows you to compile partial classes, therefore your generated code with direct references to Test will not compile even if you manage to overcome point #1
  • With the above in mind, I believe one option will be to basically have your generated code invoke reflection to obtain a reference to your desired method and invoke it dynamically (which kinda gets reduced to my suggestion #1):

    using System;
    using System.CodeDom.Compiler;
    using System.Collections.Generic;
    using System.Globalization;
    using System.Reflection;
    using System.Text;
    using Microsoft.CSharp;
    namespace ConsoleApp9
        public class MainTestClass
            private void CallTest(string value) => Console.WriteLine($"CallTest({value})");
            public object OnMethodInvoke(string functionName, List<object> parameters)
                if (functionName == "exec")
                    CSharpCodeProvider c = new CSharpCodeProvider();
                    CompilerParameters cp = new CompilerParameters();
                    cp.ReferencedAssemblies.Add("System.dll");
                    cp.ReferencedAssemblies.Add("ConsoleApp9.exe"); // note a reference to your main assembly here
                    cp.CompilerOptions = "/t:library";
                    cp.GenerateInMemory = true;
                    StringBuilder sb = new StringBuilder("");
                    sb.Append("using System;\n");
                    sb.Append("using System.Reflection;\n");
                    sb.Append("using ConsoleApp9;\n"); // reference your main assembly ConsoleApp9. this is my test console app 
                    sb.Append("namespace ScriptStack.Dynamic\n");
                    sb.Append("{\n");
                    sb.Append(" public class Code\n");
                    sb.Append(" {\n");
                    sb.Append("     private MainTestClass t;\n"); // reference your 
                    sb.Append("     public Code(MainTestClass t) { this.t = t; }\n"); // we're going to capture reference to calling MainTestClass instance and use it for reflection
                    sb.Append("     public object EvalCode()\n");
                    sb.Append("     {\n");
                    sb.Append("         // this is a very standard method of invoking private methods. see this SO answer: https://stackoverflow.com/a/135482/12339804 \n");
                    sb.Append("         var mi = typeof(MainTestClass).GetMethod(\"CallTest\", BindingFlags.NonPublic | BindingFlags.Instance);\n");
                    sb.Append("         return mi.Invoke(t, new object[]{ \"" + (string)parameters[0] + "\" });\n"); //suppose you want to pass a string to your generated C# source.
                    sb.Append("     }\n");
                    sb.Append(" }\n");
                    sb.Append("}\n");
                    var cr = c.CompileAssemblyFromSource(cp, sb.ToString());
                    if (cr.Errors.Count > 0) return null;
                    var a = cr.CompiledAssembly;
                    object o = a.CreateInstance("ScriptStack.Dynamic.Code", false, BindingFlags.Default, null, new object[] { this }, CultureInfo.InvariantCulture, null); //we have to opt for a way more overloaded CreateInstance method due to us requiring to pass a this into the Code class we're instantiating
                    var mi = o.GetType().GetMethod("EvalCode");
                    return mi.Invoke(o, null);
                throw new NotImplementedException();
        class Program
            static void Main(string[] args)
                var t = new MainTestClass();
                t.OnMethodInvoke("exec", new List<object> { "parameter 1" });
                Console.ReadKey();
            

    Thanks for contributing an answer to Stack Overflow!

    • Please be sure to answer the question. Provide details and share your research!

    But avoid

    • Asking for help, clarification, or responding to other answers.
    • Making statements based on opinion; back them up with references or personal experience.

    To learn more, see our tips on writing great answers.