Steve1999

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.



Re: Visual C# General create a anonymous method from a string?

IsshouFuuraibou

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





Re: Visual C# General create a anonymous method from a string?

Steve1999

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);

...

}






Re: Visual C# General create a anonymous method from a string?

IsshouFuuraibou

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





Re: Visual C# General create a anonymous method from a string?

Steve1999

Eric, while we're on the issue, is this a problem that's been solved in C# v3 with Lamda expressions




Re: Visual C# General create a anonymous method from a string?

IsshouFuuraibou

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





Re: Visual C# General create a anonymous method from a string?

Steve1999

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.






Re: Visual C# General create a anonymous method from a string?

IsshouFuuraibou

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#.





Re: Visual C# General create a anonymous method from a string?

IsshouFuuraibou

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 });
}







Re: Visual C# General create a anonymous method from a string?

Steve1999

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).




Re: Visual C# General create a anonymous method from a string?

IsshouFuuraibou

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