0

I want to let the user to write a block of code as text, compile it on the fly, execute it on a collection which I obtain from a data source and get the result.

I have been tying to pass a generic list as a parameter to the dynamically compiled code, but I could not figure out a way. Following is my code:

//User writes this code in a textbox and executes
var executeCode = @"//this line doesn't work because I don't know the type
                            MessageBox.Show(Parameters[0].Count());
                            //following works fine
                            var t = new List<string>{""asd"", ""xyz""};
                            var a = t.Select(x => x).First();
                            MessageBox.Show(a); 
                            return (object) a;";

#region template Code
executeCode = @"
                using System;
                using System.IO;
                using System.Windows.Forms;
                using System.Linq;
                using System.Collections.Generic;

                namespace MyNamespace {
                public class MyClass {

                public object DynamicCode(params object[] Parameters) {
                " + executeCode +
                "}   }    }";
#endregion template Code

var references = new[] { "System.dll", "System.Core.dll", "System.Windows.Forms.dll" };

var compilerParams = new CompilerParameters
            {
                GenerateInMemory = true,
                TreatWarningsAsErrors = false,
                GenerateExecutable = false,
                CompilerOptions = "/optimize"
            };
        compilerParams.ReferencedAssemblies.AddRange(references);

        var provider = new CSharpCodeProvider();
        var compile = provider.CompileAssemblyFromSource(compilerParams, executeCode);

        if (compile.Errors.HasErrors)
        {
            var text = compile.Errors.Cast<CompilerError>()
                              .Aggregate("Compile Error: ", (current, ce) => current + ("rn" + ce.ToString()));
            throw new Exception(text);
        }

        // execute the compiled code

        var assembly = compile.CompiledAssembly;
        var myObject = assembly.CreateInstance("MyNamespace.MyClass");
        if (myObject == null)
        {
            MessageBox.Show("Couldn't load class.");
            return;
        }

        var sampleList = new List<string> { "abcd", "bcd" };
        var codeParams = new object[] { sampleList };

        try
        {
            var loResult = myObject.GetType().InvokeMember("DynamicCode",BindingFlags.InvokeMethod, null, myObject, codeParams);
            MessageBox.Show("Method Call Result:\r\n\r\n" + loResult, "Compiler Demo", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
        catch (Exception loError)
        {
            MessageBox.Show(loError.Message, "Compiler Demo", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

In the above code I just pass a string list. But I will replace it with an object. User will write Linq queries to filter the collection, which I compile on the fly and return the result.

Any pointers on this will be really helpful. (I use C# 4.5)

4
  • Do you really have to make the code in such unmanageable way? There is a thing called interface and it was invented decades ago. Commented Aug 2, 2013 at 23:57
  • @sza it's my scratch code. I will fix the manageability part if it serves the purpose... otherwise why scratch more code? Commented Aug 3, 2013 at 0:00
  • You're trying to call .Count() so you can just downcast to IEnumerable<object> right away - doesn't matter what the actual class is. Commented Aug 3, 2013 at 0:01
  • that would solve the Count() but what If I want to write a lambda expression to filter the list based on say Firstname starts with A? In that case I will need the type or should I use reflection to get properties of the IEnumerable<object> and then execute the lamda expression? Commented Aug 3, 2013 at 0:03

1 Answer 1

2

There are several options.

  1. Change the type of your Parameters object to List<string>[]. This is the way to go if you always know you are passing in a List<string>.

  2. Cast in your dynamically generated code: ((List<string)Parameters[0]).Count; It is a little bit clunky, but will get rid of the error.

  3. Change the type of your Parameters object to dynamic. Since your are compiling the code at runtime, compile time type checking may not be a priority for you.

Sign up to request clarification or add additional context in comments.

1 Comment

I used the solution 1. I know the type which I pass in to the method so I always cast the parameter to the specific type.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.