Click here to Skip to main content
Click here to Skip to main content

RunSharp - Reflection.Emit Has Never Been Easier

, 10 Aug 2009 MIT
Rate this:
Please Sign up or sign in to vote.
RunSharp (or Run#) is a high-level wrapper around the Reflection.Emit API, allowing you to generate code at runtime quickly and easily.


Many developers have found the magic of emitting code at runtime using Reflection.Emit. Also, many have not used it simply because of the complexity of the API. RunSharp (or Run# if you prefer), aims to bring the simplicity and readability of a high-level language such as C# to runtime code generation.

The IL is a great language. However, there are several problems with emitting IL instructions directly. One of them is that it's quite hard to see the actual structure of the generated code, by just looking at series of Emit() calls. Also, there are strict rules as to what sequences of IL you can emit, unless you want to receive the awful 'Common Language Runtime detected an invalid program' exception. Sometimes, it's hard to find out exactly what IL is needed to achieve some simple C# concept. And there are more. I've created RunSharp to avoid these problems, and I've had a lot of fun writing it. And I hope you will have a lot of fun using it.

Using the Code

There is very little you need to know before you start emitting your next great assembly. Here are the simple steps to your first Hello World generated at runtime:

1. Create an Assembly

AssemblyGen ag = new AssemblyGen("HelloWorld.exe");

This call simply creates an AssemblyBuilder and ModuleBuilder under the hood.

2. Define Your First Class

TypeGen MyClass = ag.Public.Class("MyClass");
  // we'll continue here

As you can see, this is very similar to what you would write in C#, e.g.:

public class MyClass
  // we'll continue here

You can also use other modifiers for the class, such as Private, Sealed, Abstract or NoBeforeFieldInit (the last allows the static constructor to be invoked sooner or later than any class member is accessed - see an excellent article on beforefieldinit written by Jon Skeet for some details).

3. Define the Main() Method

CodeGen g = MyClass.Public.Static.Method(
  typeof(void), "Main", typeof(string[]));
  Operand args = g.Arg(0, "args");
  // we'll continue here

The above corresponds to the following in C#:

public static void Main(string[] args)
  // we'll continue here

Note the definition of the Operand variable here. Operand is one of the core classes in RunSharp, representing a literal, argument, local variable, expression etc. It also overloads all the operators, which can be used to construct expressions much like you would in C#. The Operand in the snippet above will represent the single argument of the main method. If you don't need to define the name of the argument (note that names of arguments are not defined in the Method() call), you can skip this altogether and simply use g.Arg(0) to access the argument.

4. Write the Body of the Method

g.If(args.ArrayLength() > 0);
  g.WriteLine("Hello " + args[0] + "!");
  g.WriteLine("Hello World!");

And compare this to the C# version:

if (args.Length > 0)
  Console.WriteLine("Hello " + args[0] + "!");
  Console.WriteLine("Hello World!");

As you can see, the code is quite similar. There are some things to note - the g.WriteLine(...) calls are a special case, and they are simply a shortcut to the longer

g.Invoke(typeof(Console), "WriteLine", ...)

you can use to call any method on any type/instance. Also, you can see the length of the arguments is retrieved by calling ArrayLength() on the args operand. This is similar to calling args.Property("Length"), although it yields a bit better performance, as it's translated to the ldlen opcode instead of accessing the property of the array object.

5. Using the Generated Code

Now that we have created a simple hello world application, you can, for example, choose to save it and run it:

// this will save the generated assembly
// to the file provided at construction time

// execute the generated assembly
  "HelloWorld.exe", null, new string[] { "John" });

Or, you can just decide to complete the assembly by calling ag.Complete() and use the generated types in any way you like (the TypeGen class returned when defining the type is implicitly convertible to Type).

Other Mini-Samples

In the hello world sample, only the simplest concepts were shown. You can do, of course, much more with RunSharp. Here are some small examples of various concepts and how they are related to C#.

1. Local Variables


int x = 3;
long y = x + 15;
string s;


Operand x = g.Local(3);
Operand y = g.Local(typeof(long), x + 15);
Operand z = g.Local(typeof(string));

Note that if the type of the variable is not specified, it automatically takes the type of the initialized expression. Also, an untyped and uninitialized variable can be created by calling g.Local() without any parameters. Such a variable will receive the type of the first expression assigned to it.

Note that it's possible to write code such as:

Operand x = g.Local(3);
Operand y = x + 40;

However, the y will not be a local in this case, but rather something like an 'alias' for the expression x + 40, meaning the expression will be evaluated every time y is used.

2. Statements

Statements are always generated using the members of the CodeGen class. Most C# keyword-based statements have their equivalent methods in RunSharp (i.e. Break() for break, Throw() for throw, Return() for return, etc.). Other than these, only assignment, method-invocation and increment/decrement statements are supported.

2.1 Assignment, Increment/Decrement


a = 3;
b = a + 5;
c += 4;


g.Assign(a, 3);
g.Assign(b, a + 5);
g.AssignAdd(c, 4);

Note that the assignment operator cannot be overridden in C#. That's why we need a dedicated method to perform the assignment. Also, there is a method for each compound assignment operator.

2.2. Method Invocation


// instance method
o.SomeMethod(arg1, arg2);
// static method of a system type
Console.WriteLine(arg1, arg2);
// static method of a custom type
MyType.MyMethod(arg1, arg2);
// delegate
d(arg1, arg2);


// instance method
g.Invoke(o, "SomeMethod", arg1, arg2);
// static method of a system type
g.Invoke(typeof(Console), "WriteLine", arg1, arg2);
// static method of a custom type
g.Invoke(MyType, "MyMethod", arg1, arg2);
// delegate
g.InvokeDelegate(d, arg1, arg2);

Note that when invoking a method on a generated type, the typeof() is omitted - this is because the TypeGen converts implicitly to Type.

3. Expressions

As already mentioned, the Operand class allows you to construct expressions much like you would in C#. There are only a few cases when the intuitive syntax is not available.

3.1 Pre/Post-Fix Operators


x = a++;
y = --b;


g.Assign(x, a.PostIncrement());
g.Assign(y, b.PreDecrement());
3.2 Conditional Operator (?Smile | :)


x = a > 0 ? b : c;


g.Assign(x, (a > 0).Conditional(b, c));

You should never use the ?: operator directly with an Operand instance as the first operand - this will corrupt the internal state of the Operand, as the operators true and false are overloaded in a way that allows the && and || operators to be used intuitively. Instead of the ?:, you can use the op.Conditional(ifTrue, ifFalse) syntax. You will also corrupt the state if you use an Operand as a condition in if, while, for, etc., but this is generally less likely, as you generate code using g.If(), etc. instead.

3.3. Member Access


// instance member access
x = o.MyMethod(arg1, arg2);
y = o.MyProperty;
o.MyProperty = z;
f = o.myField;

// static member access 
x = MyType.MyMethod(arg1, arg2);
y = Console.BackgroundColor;


// instance member access
g.Assign(x, o.Invoke("MyMethod", arg1, arg2));
g.Assign(y, o.Property("MyProperty"));
g.Assign(o.Property("MyProperty"), z);
g.Assign(f, o.Field("myField"));

// static member access
g.Assign(x, Static.Invoke(MyType, "MyMethod", arg1, arg2);
g.Assign(y, Static.Property(typeof(Console), "BackgroundColor");
3.4. Object Creation


// instance creation
MyType x = new MyType(arg1, arg2);
// array creation
string x = new string[10, 20];
// initialized array creation
int[] x = { 1, 2, 3, 4 };
// delegate creation
MyDelegate x = new MyDelegate(this.MyMethod); 


// instance creation
Operand x = g.Local(Exp.New(MyType, arg1, arg2));
// array creation
Operand x = g.Local(Exp.NewArray(typeof(string), 10, 20));
// initialized array creation
Operand x = g.Local(Exp.NewInitializedArray(typeof(int), 1, 2, 3, 4));
// delegate creation
Operand x = g.Local(Exp.NewDelegate(MyDelegate, g.This(), "MyMethod"));

Contact Me

For any questions, ideas, suggestions, etc., don't hesitate to contact me, for example through the RunSharp project here.

Happy Runtime Code Generation!


  • 2007-10-18: Original article
  • 2009-08-07: Updated source code download


This article, along with any associated source code and files, is licensed under The MIT License


About the Author

Stefan Simek
Web Developer
Slovakia Slovakia
No Biography provided

Comments and Discussions

QuestionGeneric method invokation PinmemberBoggart13-Apr-15 5:23 
AnswerAwesome!! PinmemberMember 803978614-Jul-14 14:33 
Questionparse string into runsharp sample Pinmemberproincm14-Jan-13 9:55 
GeneralMy vote of 5 [modified] PinmemberWight13-Apr-12 12:11 
GeneralMy vote of 5 PinmemberProEnggSoft22-Mar-12 1:47 
QuestionVery Cool PinmvpDave Kerr2-Mar-12 5:47 
GeneralOutput C# or VB Code instead of compiled result Pinmemberneil.hall10-Aug-09 14:35 
AnswerRe: Output C# or VB Code instead of compiled result PinmemberStefan Simek11-Aug-09 11:48 
GeneralBug (and workaround) calling virtual functions on value types Pinmemberninj4-Jan-08 7:35 
GeneralRe: Bug (and workaround) calling virtual functions on value types PinmemberStefan Simek6-Jan-08 12:01 
GeneralRe: Bug (and workaround) calling virtual functions on value types Pinmemberninj7-Jan-08 6:31 
GeneralRe: Bug (and workaround) calling virtual functions on value types Pinmemberjgauffin11-Jan-09 22:20 
GeneralRe: Bug (and workaround) calling virtual functions on value types Pinmemberninj12-Jan-09 0:21 
QuestionAttributes, and Complete() method PinmemberMarc Gravell23-Oct-07 21:47 
AnswerRe: Attributes, and Complete() method PinmemberStefan Simek24-Oct-07 2:55 
Hi Marc,

1 - Unfortunately, there is absolutely no support for attributes in this version. However, attributes are one of the features I plan to add soon.
2 - The TypeGen to Type conversion is something for what I haven't decided yet, how the interface will exactly look like - I understand that the current implementation is not very nice. However, I'd like to keep it clean, so I'm not sure if having a Complete() method, that can be called again and again and will in fact do what it's name says (Complete) the type only the first time, and it's real function would be something like 'GetConstructedTypeAndCompleteIfNeeded' Wink | ;) Currently, TypeGen is also implicitly convertible to Type, however this conversion doesn't complete the type - it returns the underlying TypeBuilder if the type hasn't been completed explicitly. Also, having an implicit conversion as the only means of retrieving the constructed type is not something I'd like to see in the 'final' version. Most likely, I'll add a new method, something like 'GetConstructedType' or 'GetSystemType' or something, maybe with an optional 'completeIfNeeded' argument.

Thanks for your feedback,
GeneralRe: Attributes, and Complete() method PinmemberMarc Gravell24-Oct-07 3:02 
QuestionWhy not going further... with Compact Framework? Pinmemberrvaquette22-Oct-07 22:30 
GeneralVery cool but... Pinmemberlaughingandliving22-Oct-07 16:25 
QuestionGeneric Support? PinmemberDah_CN21-Oct-07 21:11 
AnswerRe: Generic Support? PinmemberStefan Simek22-Oct-07 5:40 
GeneralRe: Generic Support? PinmemberDah_CN22-Oct-07 19:49 
Generalgood for lots of reasons Pinmemberdave.dolan20-Oct-07 17:51 
GeneralNow that's.... PinmemberMarc Leger20-Oct-07 7:12 
GeneralGood stuff! Pinmemberleppie19-Oct-07 14:37 
GeneralTHis look really cool. PinmemberJames Curran19-Oct-07 11:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150414.1 | Last Updated 10 Aug 2009
Article Copyright 2007 by Stefan Simek
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid