Sign in to follow this  

[.net] Dynamic event generation.

This topic is 4555 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hey, Seems im always posting here... sorry! Anyway, my app is coming really nicely now, its all plugin driven. Im having a little bit of trouble with reflection (emitting specifically). When I load a new document type into my app (which is a little IDE style thing), it creates a new menu item to create an instance of that document. I need to link up event handling to the event, and I'm not totally sure what to do. I had a look at the MethodBuilder and stuff, but I couldn't figure out how to get the IL Code. Here's what I've managed to get so far..
public void AddFiletype(FileTypes.FileType f)
{
    int index = buttonStandardNew.Items.Add(f.Name);
    TD.SandBar.MenuButtonItem item = buttonStandardNew.Items[index];
    
    MethodBuilder mb = new MethodBuilder();
    mb.CreateMethodBody( .... now im lost .... );

    Delegate eventDelegate = Delegate.CreateDelegate(typeof(EventArgs), mb);
}


Its probably pretty simple stuff, but I'm not sure about it. I guess I need to use the CSharpCompiler and stuff? Any help appreciated!

Share this post


Link to post
Share on other sites
I also do some runtime code generation, so I may help you.

In .NET 1.1, methods can't live in 'void' like that. To enforce that fact, you'll observe that MethodBuilder has no public c'tor, so you can't create an instance like you did.

You have to first create a module, add a class, then add methods to this class. Some (painful) code:

// Create a new assembly
AssemblyName myAsmName = new AssemblyName("myAssembly");
AssemblyBuilder myAsmBuilder = Thread.GetDomain().DefineDynamicAssembly(myAsmName, AssemblyBuilderAccess.Run);
// Add a module to the assembly
ModuleBuilder myModule = myAsmBuilder.DefineDynamicModule("myModule");
// Create a new type
TypeBuilder myClass = myModule.DefineType("myType", /* lookup other paramteres in help */);
// Add a method
MethodBuilder myMethod = myClass.DefineMethod("myMethod", /* ... */);
// Generate the method body
ILGenerator il = myMethod.GetILGenerator();
il.Emit(/* ... */); // create all the code
// When everything is done, create the new type
Type myType = myClass.CreateClass



If you're using .NET 2.0, you have a new way, called LCG for Lightweight Code Generation. It allows you to generate new methods on the fly, without the need to have assembly, modules, types, etc.

DynamicMethod dm = new DynamicMethod("myMethod", /* ... */);
ILGenerator il = dm.GetILGenerator();
il.Emit(/* ... */);

// Can get invoked:
dm.Invoke(null, null);
// or create a delegate:
Delegate d = dm.CreateDelegate(delegateType);



As you see, it is a lot easier, so I recommand you to use .NET 2.0 if you can.

But I recommand you even more to consider using another design patter. According to your post, the delegate should just create a new instance of the document. Wouldn't some Factory design pattern solve the problem in a lot better way ?
Dynamically generating code may be hard, and it's can be a real pain to debug.

Have a nice day,
jods

Share this post


Link to post
Share on other sites
Well, I need to add the new filetype into the toolbar and menus. I am doing it was runtime code beucause I don't believe the file type should be responsible for creating menus - that is the job of the registration class.

My main problem is how to I create the code though? Emit requires IL opcodes, is there any way to gerenate IL code from c# at runtime?

Oh and yea, this is .NET 2 :)

Share this post


Link to post
Share on other sites
If you are using dotnet 2, anonymous delegates may be a way to avoid having to emit IL. Here is an example of what you can do:


class SomeClass {
static int classScope = 0;
int instanceScope = 0;
void ConnectMenu(MenuItem item){
int outterMethodScoped = 0;
item.Click += delegate(object sender, EventArgs e){
Console.WriteLine(classScope++);
Console.WriteLine(instanceScope++);
Console.WriteLine(outterMethodScope++);
}
}
}



In other words, you can generate a delegate inside a method call and it can access/be configured by variables form a number of different scopes. Much, much easier than generating IL and perhaps powerful enough to do what you want.

Share this post


Link to post
Share on other sites
Quote:
Well, I need to add the new filetype into the toolbar and menus. I am doing it was runtime code beucause I don't believe the file type should be responsible for creating menus - that is the job of the registration class.


I agree. I don't know how you system is designed, but here are a few suggestions to get you started.

1. I'm sure you have some kind of abstract class or interface, probably named Document, which is the ancestor of every "document" you may load in your interface.

abstract class Document
{
/* Common documents methods/fields/properties go here */
}



2. Make your plugins implement some kind of IDocumentProvider interface:

interface IDocumentProvider
{
Document CreateDocument();
}

// ----------------- Below is sample plugin code ---------------
class MySamplePluginDocument : Document
{
/* Specific document implementation */
}

class MySamplePlugin : IDocumentProvider
{
public Document CreateDocument()
{ return new MySamplePluginDocument(); }
}



3. Finally, use this interface to create your document in your GUI class:

class GUI /* : maybe Form or whatever */
{
void AddPlugin(IDocumentProvider plugin)
{
MenuItem mItem;
/* Create a new menu item, name it, add it to your main menu, etc. */
mItem.Click += NewDocument;
// This is NOT a best practice, but for simplicity I'll just save to plugin into the Tag property of the menu item.
mItem.Tag = plugin;
}

void NewDocument(object sender, EventArgs e)
{
// Retrieve and type our objects
MenuItem mItem = (MenuItem)sender;
IDocumentProvider plugin = (IDocumentProvider)mItem.Tag;
// Create the new document
Document newDoc = plugin.CreateDocument();
/* Add it to the interface, etc. */
}
}



As you see, you can reuse the same method for the event handler! Moreover everything is statically typed, easy to debug, and has good runtime performances.

You should always try to design things nicely first. Runtime code generation should be kept for very special purposes, e.g. compiling scripts to native code.

Hope it helps,
jods

Share this post


Link to post
Share on other sites
Cool stuff Jods, Ill probably go with your last idea. I always feel better using well designed patterns, rather than 'hacky' things like runtime generation (which as you said doesn't really have its place here).

Share this post


Link to post
Share on other sites

This topic is 4555 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this