none
create a anonymous method from a string?

    Question

  • Is it possible to build a delegate/anonymous method from a string?

               public static List<T> Project<T>(List<T> list, Predicate<T> pred) {...}

     ...
     string dynamicCode = "delegate(Person p) { if (p.LastName == \"Neward\") return true; else return false; }";
     Project<Person>(l, CreateDelegate(dynamicCode);

    So that the delegate can be passed in to this method? This would be very helpful to me.

    Thanks.

    Thursday, November 08, 2007 11:52 PM

Answers

  • The answer is no, you can't create an anonymous method from a string. The reason being is that anonymous methods are converted during compile time, anonymous methods are there to improve the code. When compiled to IL anonymous methods aren't "anonymous" anymore, they will have a compiler generated name. Thus if you really wanted to you can use reflections and make a call to the anonymous method.

    However you can build a method and delegate at runtime then deal with returning that new code in such a way that you can call it. This however is not a simple task, as you're basically building, compiling, and calling C# code at runtime.

    Can you tell us why you want to convert from a string to a delegate?
    Friday, November 09, 2007 10:33 PM

All replies

  • The answer is no, you can't create an anonymous method from a string. The reason being is that anonymous methods are converted during compile time, anonymous methods are there to improve the code. When compiled to IL anonymous methods aren't "anonymous" anymore, they will have a compiler generated name. Thus if you really wanted to you can use reflections and make a call to the anonymous method.

    However you can build a method and delegate at runtime then deal with returning that new code in such a way that you can call it. This however is not a simple task, as you're basically building, compiling, and calling C# code at runtime.

    Can you tell us why you want to convert from a string to a delegate?
    Friday, November 09, 2007 10:33 PM
  • We store "association expressions" in a database (as strings). We use these to dynamically associate documents to particular workflow actions.  These expressions we want to compile dynamically and evaluate against a rich type in our system.

    Why doesnt .Net support the ability to evaluate dynamic expressions?  Take the below code as an example. This is what i'd like to do (where the method body came from the database as a string)

    public delegate int delAdd(int Num1, int Num2);

     

    public void ExecuteCode()

    {

     

        string MethodBody =

            @"Num1 += 10;

            Num2 += 10;

            return  Num1 * Num2;";

     

        Type[] parameterTypes = { typeof(int),typeof(int) };

        DynamicMethod dm = new DynamicMethod("MultiplySpecial",

                                   typeof(int), parameterTypes,

                                   this.GetType(),
                                   MethodBody,

                                   CodeDomProvider.CreateProvider("CSharp"));           

     

     

        delAdd AddMethod = dm.CreateDelegate(typeof(delAdd));

        int Result = AddMethod(10, 20);

        ...

    }


    Monday, November 12, 2007 9:58 PM
  • It sounds like you really need to explore more into what you can do with System.CodeDom and System.Reflection.Emit, as those are the tools you can use to dynamically build and compile code.

    There's a good article on MSDN about it: Dynamic Source Code Generation. Your situation may need to utilize both CodeDom and Emit to work within the framework to generate the dynamic code. Have you done work with Emit and CodeDom without having much success?
    Monday, November 12, 2007 11:14 PM
  • Eric, while we're on the issue, is this a problem that's been solved in C# v3 with Lamda expressions?
    Tuesday, November 13, 2007 1:19 AM
  • no, you wouldn't be able to create a lamda expression from a string during runtime either, you would still need to work with CodeDom/Emit to get the actual code generated.

    Lambda expressions are really a nice adaptation to anonymous delegates/methods that make the syntax for LINQ simpler. There are some differences, but nothing in the direction that you'd want (mainly with parameter type inference being allowed).

    Lambda expressions vs Anonymous Methods (Part 2) (Part 3) and Scott Guthrie's blog on it
    Tuesday, November 13, 2007 4:46 PM
  • I looked at the codedom/emit stuff.  It looks like you have to work with IL.  I've been having problems trying to codeDOM compile code to grab the methodstate/info.  I keep getting random threadaborts/or protected memory exceptions.

    All this sure seems like a lot of work for a simple task (dynamically evaluate a c# method body). As I mentioned, we need a way to dynamically evaluate an expression against a rich type using an expression that is created (typed in and validated) by our users. 

    Are there any other ideas to explore?  is there any type of .Net script support i can call with a string like this:

    "bool Evaluate(VaultObject obj)  
    {
       return obj.Format.StartsWith(@"Foobar");
    }"

    Where the VaultObject is a rich .Net type defined in a referenced DLL and the red is the expression from the db?

    We went down this path because using C# expression would allow us to express boolean logic easily on a incoming VaultObject.


    Tuesday, November 13, 2007 11:45 PM
  • Well unfortunately C# is not a scripting language. However if your interested both ruby and python (IronRuby and IronPython respectively) have implementations for .Net framework and may be more flexible in this mattter.

    remember that
    "
    bool Evaluate(VaultObject obj)
    {
    return obj.Format.StartsWith(@"FooBar");
    }
    "
    has no meaning on it's own, however
    "
    namespace Dynamic
    {
    class DynamicCode
    {
    bool Evaluate(VaultObject obj)
    {
    return obj.Format.StartsWith(@"FooBar");
    }
    }
    }
    "
    does have meaning, since C# is object oriented, you'll need to wrap anything you build dynamically in a class. Adding the namespace gives you something to reference from another namespace (your running program).

    Now one question is during the execution of the program, are new functions added to the database or are these functions static during execution (users can't add to, or remove from, the list of functions)?

    I'll do some test cases later today to see if I can't get a simple code generation from a string working as an example. It looks like CodeDom does support the CSharpCodeDomProvider for processing C#.
    Wednesday, November 14, 2007 12:04 AM
  • Alright, time for some code that generates a dll at runtime, loads that very dll, then calls a method (static or not) from the type inside that dll.

    Code Block

    using System;
    using System.CodeDom;
    using System.CodeDom.Compiler;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Drawing;
    using System.Linq;
    using System.Reflection;
    using System.Reflection.Emit;
    using System.Text;
    using System.Windows.Forms;

    namespace TestApp
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }

            SampleLib.SampleType test = new SampleLib.SampleType();

            private void button1_Click(object sender, EventArgs e)
            {
                // Dynamically build and call the method
                label1.Text = test.MyText;
            }

            private void button2_Click(object sender, EventArgs e)
            {
                StringBuilder DynamicCode = new StringBuilder();
                DynamicCode.Append("namespace TestDynamic");
                DynamicCode.Append("{");
                DynamicCode.Append("public class DynamicCode");
                DynamicCode.Append("{");
                DynamicCode.Append("public static void EditText(SampleLib.SampleType t)");
                DynamicCode.Append("{");
                DynamicCode.Append("t.MyText = \"Goodbye!\";");
                DynamicCode.Append("}");
                DynamicCode.Append("}");
                DynamicCode.Append("}");

                string CodeString = DynamicCode.ToString();

                System.IO.FileInfo fi = new System.IO.FileInfo(Application.ExecutablePath);
                CodeDomProvider provider = CodeDomProvider.CreateProvider("C#");
                CompilerParameters CompileParams = new CompilerParameters(new string[] { fi.DirectoryName + "\\SampleLib.dll" },
                    fi.DirectoryName + "\\Dynamic.dll");
                CompileParams.MainClass = "DynamicCode";
                CompileParams.GenerateExecutable = false;
                //CompileParams.GenerateInMemory = true;
                CompilerResults r = provider.CompileAssemblyFromSource(CompileParams, new string[] {CodeString});
                foreach (CompilerError er in r.Errors)
                {
                    Console.WriteLine(er.ErrorText);
                }
            }

            private void button3_Click(object sender, EventArgs e)
            {
                // Dynamically call assembly
                System.IO.FileInfo fi = new System.IO.FileInfo(Application.ExecutablePath);
                Assembly dynAsm = Assembly.LoadFile(fi.DirectoryName + "\\Dynamic.dll");
                if (dynAsm != null)
                {
                    object o = dynAsm.CreateInstance("TestDynamic.DynamicCode", true);
                    Type t = dynAsm.GetType("TestDynamic.DynamicCode");
                    t.GetMethod("EditText").Invoke(o, new object[]{test});
                }
            }
        }
    }


    A few things to note, SampleLib is a simple project with a single type that has a single property (MyText) inside it. Note that I have to reference it from the new dll, you'll have to use full paths for output and referenced dlls. This test project was just a simple WinForm with a label and three buttons to make sure I can compile, call, and then see the change occur, if you would like the sample project/exe, just give me an email (it's in my profile). Does this help you any? It should be fairly simple to adapt the concept here to work with parts of it (or all of it) being recovered from a database. This does have security implications with the dll being created as a file, someone technically could replace it with a dll and run their own code instead. So it would probably be worth it to look into making it compile in memory.

    Compiling to memory is pretty simple:
    Don't define an ouput file
    set the flag:
    CompileParams.GenerateInMemory = true;
    your assembly will reside in the CompilerResults CompiledAssembly property (r.CompiledAssembly in this case)

    Code Block

                StringBuilder DynamicCode = new StringBuilder();
                DynamicCode.Append("namespace TestDynamic");
                DynamicCode.Append("{");
                DynamicCode.Append("public class DynamicCode");
                DynamicCode.Append("{");
                DynamicCode.Append("public static void EditText(SampleLib.SampleType t)");
                DynamicCode.Append("{");
                DynamicCode.Append("t.MyText = \"Goodbye!\";");
                DynamicCode.Append("}");
                DynamicCode.Append("}");
                DynamicCode.Append("}");

                string CodeString = DynamicCode.ToString();

                System.IO.FileInfo fi = new System.IO.FileInfo(Application.ExecutablePath);
                CodeDomProvider provider = CodeDomProvider.CreateProvider("C#");
                CompilerParameters CompileParams = new CompilerParameters(new string[] { fi.DirectoryName + "\\SampleLib.dll" });
                CompileParams.MainClass = "DynamicCode";
                CompileParams.GenerateExecutable = false;
                CompileParams.GenerateInMemory = true;
                CompilerResults r = provider.CompileAssemblyFromSource(CompileParams, new string[] { CodeString });
                foreach (CompilerError er in r.Errors)
                {
                    Console.WriteLine(er.ErrorText);
                }
                Assembly asm = r.CompiledAssembly;
                if (asm != null)
                {
                    object o = asm.CreateInstance("TestDynamic.DynamicCode", true);
                    Type t = asm.GetType("TestDynamic.DynamicCode");
                    t.GetMethod("EditText").Invoke(o, new object[] { test });
                }


    Wednesday, November 14, 2007 4:05 AM
  • Eric,

    Thanks for your source.  This is what i've been doing.  The only diff is that this runs in a WCF service processs.

    When i "Invoke", i get thread aborts (or end of stack frame errors). 
    Wednesday, November 14, 2007 8:31 PM
  • Interesting, I haven't done much with WCF, are you needing to call these over WCF or are the methods called by the WCF service process? It's possible that you'll need some extra steps to work with the WCF service, putting a level of abstraction between the WCF and the processing of the dynamic methods.

    With a end of stack frame error it soulds like you're consuming more from the stack than is put onto the stack, is there any method call that's taking more parameters than you're passing it? How complex are the functions that you're compiling dynamically?
    Wednesday, November 14, 2007 8:46 PM