HelloWorld

BLToolkit.NET

Business Logic Toolkit for .NET www.bltoolkit.net
 

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:

HelloWorld.cs
using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;

using NUnit.Framework;

namespace Examples.Reflection.Emit
{
    [TestFixture]
    public class HelloWorldNormal
    {
        public interface IHello
        {
            void SayHello(string toWhom);
        }

        [Test]
        public void Test()
        {
            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();

            // string.Format("Hello, {0} World!", toWhom)
            //
            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) }));

            // Console.WriteLine("Hello, World!");
            //
            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.

Fortunately, there is an alternative way. BLToolkit 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 source code that looks similar to MSIL.

The following examples show how to use the EmitHelper class with C#, VB.NET, and C++/CLI.

HelloWorld.cs
using System;

using NUnit.Framework;

using BLToolkit.Reflection;
using BLToolkit.Reflection.Emit;

namespace Examples.Reflection.Emit
{
    [TestFixture]
    public class HelloWorld
    {
        public interface IHello
        {
            void SayHello(string toWhom);
        }

        [Test]
        public void Test()
        {
            EmitHelper emit = new AssemblyBuilderHelper("HelloWorld.dll")
                .DefineType  ("Hello", typeof(object), typeof(IHello))
                .DefineMethod(typeof(IHello).GetMethod("SayHello"))
                .Emitter;

            emit
                // string.Format("Hello, {0}!", toWhom)
                //
                .ldstr   ("Hello, {0}!")
                .ldarg_1
                .call    (typeof(string), "Format", typeof(string), typeof(object))

                // Console.WriteLine("Hello, World!");
                //
                .call    (typeof(Console), "WriteLine", typeof(string))
                .ret()
                ;

            Type type = emit.Method.Type.Create();

            IHello hello = (IHello)TypeAccessor.CreateInstance(type);

            hello.SayHello("World");
        }
    }
}
Cool!!! Isn't it?

HelloWorld.vb
Imports System
Imports NUnit.Framework

Imports BLToolkit.Reflection
Imports BLToolkit.Reflection.Emit

Namespace Examples.Reflection.Emit

    <TestFixture()> _
    Public Class HelloWorld

        Public Interface IHello
            Sub SayHello(ByVal toWhom As String)
        End Interface

        <Test()> _
        Sub Test()
            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

            ' string.Format("Hello, {0} World!", toWhom)
            '
            emit _
            .ldstr("Hello, {0} World!") _
            .ldarg_1 _
            .call(GetType(String), "Format", GetType(String), GetType(Object))

            ' Console.WriteLine("Hello, World!");
            '
            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 Class

End Namespace
HelloWorld.cpp
#include "stdafx.h"

using namespace System;

using namespace NUnit::Framework;

using namespace BLToolkit::Reflection;
using namespace BLToolkit::Reflection::Emit;

namespace Examples {
namespace Reflection {
namespace Emit
{
    [TestFixture]
    public ref class HelloWorld
    {
    public:

        interface class IHello
        {
            void SayHello(String ^toWhom);
        };

        [Test]
        void Test()
        {
            AssemblyBuilderHelper ^assembly = gcnew AssemblyBuilderHelper("HelloWorld.dll");
            
            EmitHelper ^emit = assembly
                ->DefineType  ("Hello", Object::typeid, IHello::typeid)
                ->DefineMethod(IHello::typeid->GetMethod("SayHello"))
                ->Emitter;

            emit
                // string.Format("Hello, {0} World!", toWhom)
                //
                ->ldstr   ("Hello, {0} World!")
                ->ldarg_1
                ->call    (String::typeid, "Format", String::typeid, Object::typeid)

                // Console.WriteLine("Hello, World!");
                //
                ->call    (Console::typeid, "WriteLine", String::typeid)
                ->ret()
                ;

            Type ^type = emit->Method->Type->Create();

            IHello ^hello = (IHello^)TypeAccessor::CreateInstance(type);

            hello->SayHello("C++");
        }
    };
}}}
 
© 2010 www.bltoolkit.net
[email protected]