Introduction
The System.Reflection.Emit
namespace provides classes to create dynamic assemblies at runtime. It allows compilers and tools to emit MSIL, execute it and store it to a disk. Although Emit
is a powerful tool, it is also extremely hard to use.
Let us take a look at the following example, which demonstrates the "normal" way of Emit
programming:
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;
namespace EmitDemo
{
public interface IHello
{
void SayHello(string toWhom);
}
class Program
{
static void Main(string[] args)
{
AssemblyName asmName = new AssemblyName();
asmName.Name = "HelloWorld";
AssemblyBuilder asmBuilder =
Thread.GetDomain().DefineDynamicAssembly
(asmName, AssemblyBuilderAccess.RunAndSave);
ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule
("HelloWorld");
TypeBuilder typeBuilder = modBuilder.DefineType(
"Hello",
TypeAttributes.Public,
typeof(object),
new Type[] { typeof(IHello) });
MethodBuilder methodBuilder = typeBuilder.DefineMethod("SayHello",
MethodAttributes.Private | MethodAttributes.Virtual,
typeof(void),
new Type[] { typeof(string) });
typeBuilder.DefineMethodOverride(methodBuilder,
typeof(IHello).GetMethod("SayHello"));
ILGenerator il = methodBuilder.GetILGenerator();
il.Emit(OpCodes.Ldstr, "Hello, {0} World!");
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Call, typeof(string).GetMethod
("Format", new Type[] { typeof(string), typeof(object) }));
il.Emit(OpCodes.Call, typeof(Console).GetMethod
("WriteLine", new Type[] { typeof(string) }));
il.Emit(OpCodes.Ret);
Type type = typeBuilder.CreateType();
IHello hello = (IHello)Activator.CreateInstance(type);
hello.SayHello("Emit");
}
}
}
Note that the Emit
method takes one parameter as an OpCode
and optionally another one, which does not depend on the current operation context. So, this way is not entirely type safe.
Alternative
Fortunately, there is an alternative way. Business Logic Toolkit for .NET provides a helper class, EmitHelper, which can make your life a little bit easy. It contains typed wrapper methods for almost all Emit
commands and allows writing of source code that looks similar to MSIL.
The following examples show how to use the EmitHelper
class with C#, VB.NET, and C++/CLI.
Examples
C#
using System;
using BLToolkit.Reflection;
using BLToolkit.Reflection.Emit;
namespace EmitHelperDemo
{
public interface IHello
{
void SayHello(string toWhom);
}
class Program
{
static void Main(string[] args)
{
EmitHelper emit = new AssemblyBuilderHelper("HelloWorld.dll")
.DefineType ("Hello", typeof(object), typeof(IHello))
.DefineMethod(typeof(IHello).GetMethod("SayHello"))
.Emitter;
emit
.ldstr ("Hello, {0} World!")
.ldarg_1
.call (typeof(string), "Format", typeof(string),
typeof(object))
.call (typeof(Console), "WriteLine", typeof(string))
.ret()
;
Type type = emit.Method.Type.Create();
IHello hello = (IHello)TypeAccessor.CreateInstance(type);
hello.SayHello("C#");
}
}
}
VB.NET
Imports BLToolkit.Reflection
Imports BLToolkit.Reflection.Emit
Public Module Module1
Public Interface IHello
Sub SayHello(ByVal toWhom As String)
End Interface
Sub Main()
Dim assemblyHelper As AssemblyBuilderHelper = _
New AssemblyBuilderHelper("HelloWorld.dll")
Dim typeHelper As TypeBuilderHelper = _
assemblyHelper.DefineType("Hello", GetType(Object), GetType(IHello))
Dim methodHelper As MethodBuilderHelper = _
typeHelper.DefineMethod(GetType(IHello).GetMethod("SayHello"))
Dim emit As EmitHelper = methodHelper.Emitter
emit _
.ldstr("Hello, {0} World!") _
.ldarg_1 _
.call(GetType(String), "Format", GetType(String), GetType(Object))
emit _
.call(GetType(Console), "WriteLine", GetType(String)) _
.ret()
Dim type As Type = typeHelper.Create()
Dim hello As IHello = TypeAccessor.CreateInstance(type)
hello.SayHello("VB")
End Sub
End Module
C++/CLI
#include "stdafx.h"
using namespace System;
using namespace BLToolkit::Reflection;
using namespace BLToolkit::Reflection::Emit;
public interface class IHello
{
void SayHello(String ^toWhom);
};
void main()
{
AssemblyBuilderHelper ^assembly = gcnew AssemblyBuilderHelper
("HelloWorld.dll");
EmitHelper ^emit = assembly
->DefineType ("Hello", Object::typeid, IHello::typeid)
->DefineMethod(IHello::typeid->GetMethod("SayHello"))
->Emitter;
emit
->ldstr ("Hello, {0} World!")
->ldarg_1
->call (String::typeid, "Format", String::typeid, Object::typeid)
->call (Console::typeid, "WriteLine", String::typeid)
->ret()
;
Type ^type = emit->Method->Type->Create();
IHello ^hello = (IHello^)TypeAccessor::CreateInstance(type);
hello->SayHello("C++");
}