Click here to Skip to main content
12,501,010 members (50,209 online)
Click here to Skip to main content
Add your own
alternative version

Stats

56.6K views
60 bookmarked
Posted

Emit with a human face

, 25 Oct 2006
Rate this:
Please Sign up or sign in to vote.
A wrapper for the System.Reflection.Emit namespace

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();

            // 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.

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

        ' 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 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
        // 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++");
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Igor Tkachev
Architect
United States United States
No Biography provided

You may also be interested in...

Pro
Pro

Comments and Discussions

 
Questionnot available Pin
filmee2422-Mar-16 9:32
memberfilmee2422-Mar-16 9:32 
GeneralThanks for the article Pin
Miguel Barros31-Aug-09 10:54
memberMiguel Barros31-Aug-09 10:54 
QuestionJust instance of type IHello ? Pin
Stuard25-Aug-09 15:16
memberStuard25-Aug-09 15:16 
QuestionHow to emit a generic type? Pin
Liu Junfeng6-Jun-06 19:12
memberLiu Junfeng6-Jun-06 19:12 
AnswerRe: How to emit a generic type? [modified] Pin
Igor Tkachev8-Jun-06 7:25
memberIgor Tkachev8-Jun-06 7:25 
Generalvery clever Pin
flaming red dingo6-Jun-06 13:04
memberflaming red dingo6-Jun-06 13:04 
GeneralBrilliant! [modified] Pin
Mike_V21-May-06 15:57
memberMike_V21-May-06 15:57 
GeneralRe: Brilliant! [modified] Pin
BillWoodruff27-May-06 22:26
memberBillWoodruff27-May-06 22:26 
Mike,

I agree with your suggestion that a little explanation of how native-Emit works would make this a better article.

However, I disagree with your implication this might be an advertisement : Igor's work is free, includes source code, and has an unlimited license :

http://www.bltoolkit.net/license.htm

"Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:"

So, I think there is nothing commercial about his work, that it reflects his intellectual generosity and willingness to share with his technical peers, and I am very grateful to him for this opportunity to learn more.

Bill Woodruff

"The greater the social and cultural distances between people, the more magical the light that can spring from their contact." Milan Kundera in Testaments Trahis
GeneralRe: Brilliant! [modified] Pin
Igor Tkachev29-May-06 9:37
memberIgor Tkachev29-May-06 9:37 
GeneralRe: Brilliant! [modified] Pin
Mike_V29-May-06 11:09
memberMike_V29-May-06 11:09 

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.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160919.1 | Last Updated 25 Oct 2006
Article Copyright 2006 by Igor Tkachev
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid