Click here to Skip to main content
15,446,510 members
Articles / Programming Languages / MSIL
Posted 17 Oct 2007


118 bookmarked

RunSharp - Reflection.Emit Has Never Been Easier

Rate me:
Please Sign up or sign in to vote.
4.92/5 (48 votes)
10 Aug 2009MIT5 min read
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 (?:)


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

Written By
Web Developer
Slovakia Slovakia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

QuestionGeneric method invokation Pin
Boggart13-Apr-15 4:23
MemberBoggart13-Apr-15 4:23 
AnswerAwesome!! Pin
Member 803978614-Jul-14 13:33
MemberMember 803978614-Jul-14 13:33 
Questionparse string into runsharp sample Pin
proincm14-Jan-13 8:55
Memberproincm14-Jan-13 8:55 
GeneralMy vote of 5 Pin
AlexeyYakovlev13-Apr-12 11:11
professionalAlexeyYakovlev13-Apr-12 11:11 
GeneralMy vote of 5 Pin
ProEnggSoft22-Mar-12 0:47
MemberProEnggSoft22-Mar-12 0:47 
QuestionVery Cool Pin
Dave Kerr2-Mar-12 4:47
mentorDave Kerr2-Mar-12 4:47 
GeneralOutput C# or VB Code instead of compiled result Pin
NeverJustHere10-Aug-09 13:35
MemberNeverJustHere10-Aug-09 13:35 
AnswerRe: Output C# or VB Code instead of compiled result Pin
Stefan Simek11-Aug-09 10:48
MemberStefan Simek11-Aug-09 10:48 
GeneralBug (and workaround) calling virtual functions on value types Pin
ninj4-Jan-08 6:35
Memberninj4-Jan-08 6:35 
GeneralRe: Bug (and workaround) calling virtual functions on value types Pin
Stefan Simek6-Jan-08 11:01
MemberStefan Simek6-Jan-08 11:01 
GeneralRe: Bug (and workaround) calling virtual functions on value types Pin
ninj7-Jan-08 5:31
Memberninj7-Jan-08 5:31 
GeneralRe: Bug (and workaround) calling virtual functions on value types Pin
jgauffin11-Jan-09 21:20
Memberjgauffin11-Jan-09 21:20 
GeneralRe: Bug (and workaround) calling virtual functions on value types Pin
ninj11-Jan-09 23:21
Memberninj11-Jan-09 23:21 
QuestionAttributes, and Complete() method Pin
Marc Gravell23-Oct-07 20:47
MemberMarc Gravell23-Oct-07 20:47 
AnswerRe: Attributes, and Complete() method Pin
Stefan Simek24-Oct-07 1:55
MemberStefan Simek24-Oct-07 1:55 
GeneralRe: Attributes, and Complete() method Pin
Marc Gravell24-Oct-07 2:02
MemberMarc Gravell24-Oct-07 2:02 
QuestionWhy not going further... with Compact Framework? Pin
rvaquette22-Oct-07 21:30
Memberrvaquette22-Oct-07 21:30 
GeneralVery cool but... Pin
JakeSays22-Oct-07 15:25
MemberJakeSays22-Oct-07 15:25 
QuestionGeneric Support? Pin
Adrian Huang21-Oct-07 20:11
MemberAdrian Huang21-Oct-07 20:11 
AnswerRe: Generic Support? Pin
Stefan Simek22-Oct-07 4:40
MemberStefan Simek22-Oct-07 4:40 
GeneralRe: Generic Support? Pin
Adrian Huang22-Oct-07 18:49
MemberAdrian Huang22-Oct-07 18:49 
Generalgood for lots of reasons Pin
dave.dolan20-Oct-07 16:51
Memberdave.dolan20-Oct-07 16:51 
GeneralNow that's.... Pin
Marc Leger20-Oct-07 6:12
MemberMarc Leger20-Oct-07 6:12 
GeneralGood stuff! Pin
leppie19-Oct-07 13:37
Memberleppie19-Oct-07 13:37 
GeneralTHis look really cool. Pin
James Curran19-Oct-07 10:07
MemberJames Curran19-Oct-07 10:07 

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

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