Late Binding and On-the-Fly Code Generation Using Reflection in C#

Environment: .NET, C#

Introduction

In this article we will learn how to achieve late binding and how to create and execute code on the fly (during runtime) by using the Refection namespace of C Sharp in .NET. For an introduction and how to use the Reflection namespace, please refer to my previous article, "An Introduction to Reflection in C#."

Late Binding

In this example, we will see how to reflect types at run time, late bind to code, and dynamically emit and execute MSIL code. Assume that we need to develop a Peer-to-Peer (P2P) file sharing network that supports multiple protocols such as Napster, Kazaa, and eDonkey integrated with it, and we need to give the user the option to select a particular protocol. We will have these protocols in DLLs (assemblies) and we will list all supported protocols to user to select. This is analogous to calling the Win32 LoadLibrary to load a particular library and calling GetProcAddress to get the member functions. (The compiler knows nothing about these calls during compile time all are done during run time, "late binding.")

Let's start coding. First, create a CommonP2P class. This is the base class for all protocols. Enter the following code to this file, as shown in Listing 1.

Listing 1

Command line: Compile with the following command line
csc /t:library CommonP2P.cs
using System;

public abstract class CommonP2P
{
  // search string to find different dlls.
  Public static string strSearchDLL = "CommonP2P*.dll";
}

Compile this class to a DLL assembly so that we can refer to this in the forthcoming classes. Use the following command line options for compilation. This is also shown in the comments part of each class:

csc /t:library CommonP2P.cs 

Now, create CommonP2Pnapster.cs, CommonP2PEDonkey.cs, and CommonP2Pkazaa.cs (see Listings 2, 3, and 4). These protocol classes are derived from the CommonP2P.cs base class. Codes for these classes are shown below. Compile these classes to the DLL. Because we derive these classes from the CommonP2P class, we need to refer this during compilation with the /r option.

Listing 2

Command line: Compile with the following command line
csc /t:library CommonP2Pnapster.cs /r:CommonP2P.dll
using System;

public class CommonP2Pnapster : CommonP2P
{

  public static void MyMsg()
  {
    Console.WriteLine("\t CommonP2Pnapster: Protocol Napster Loaded
                      Successfully.");
  }
}

Listing 3

Command line: Compile with the following command line
csc /t:library CommonP2Pkazaa.cs /r:CommonP2P.dll
using System;

public class CommonP2Pkazaa : CommonP2P
{

  public static void MyMsg()
  {
    Console.WriteLine("\t CommonP2Pkazaa: Protocol KaZaA Loaded
                      Successfully.");
  }
}

Listing 4

Command line: Compile with the following command line
csc /t:library CommonP2PEDonkey.cs /r:CommonP2P.dll

using System;

public class CommonP2PEDonkey : CommonP2P
{
  public static void MyMsg()
  {
    Console.WriteLine("\t CommonP2PEDonkey: Protocol eDonkey
                      Loaded Successfully.");
  }
}

Now, we will create a console application to test these DLLs with reflection. Add the codes to the main function, as shown in Listing 5.

Listing 5

using System;
using System.Reflection;
using System.IO;

static void Main()
{
string[] filenames = Directory.GetFiles
                    (Environment.CurrentDirectory,
                     CommonP2P.strSearchDLL);

foreach (string filename in filenames)
{
Console.WriteLine("Loading Protocol: {0}", filename);

  //Load the Assembly
  Assembly a = Assembly.LoadFrom(filename);

  // get all the types in the loaded assembly
  Type[] types = a.GetTypes();

  foreach (Type typ in types)
  {
    // dynamically create or activate(if exist) object
    object obj = Activator.CreateInstance(typ);

    // get required method by specifying name
    MethodInfo mi = typ.GetMethod("MyMsg");
    mi.Invoke(0, null);

    // or use this line of code instead of above two lines.
    // typ.InvokeMember("MyMsg", BindingFlags.InvokeMethod,
                         null, 0,
new Object[]{})
  }
}
}

Note that the using section contains a Reflection namespace for reflection operations and an IO namespace for file searching. We need to refer CommonP2P.dll to this project because we are using the strSearchDLL member variable for the DLL mask string for the search criteria. The GetFiles member function of the System.IO.Directory class returns an array or string of filenames that are matching for the given criteria and in the given directory. Here it's the application exe directory. We load the assembly dynamically and call its member functions.

Load the assembly using Assembly.LoadFrom by giving the full path of the assembly; then get all types of an assembly by using the Assembly.GetTypes method. Using this type, we need to create the objects dynamically. For this purpose, we can use the Activator class. The activator class is used to dynamically create or activate (if already existing) a type. The CreateInstance member of the Activator class creates an instance of the type defined in an assembly by invoking the constructor that best matches the given arguments. If no arguments are passed, it calls the default constructor. If it successfully created an object dynamically, this member function returns a reference to that object. Once the object has been created, call the GetMethod method of the Type class by giving the method name string as a parameter. This method returns the MethodInfo object; MethodInfo discovers the attributes of a method and provides access to the metadata method. Then, call the Invoke method of the MethodInfo class. This calls the DLL's MyMsg. Alternatively, we also can call the InvokeMember function of the Type class by specifying required parameter, as shown in Listing 5.

Creating and Executing Code On the Fly (Runtime)

In this example, we will see something about how to create code on the fly. By using namespace System.Reflection.Emit, we can create types at runtime. With this namespace we can dynamically:

  1. Define an assembly in memory.
  2. Create a module for an assembly.
  3. Define types (classes) for a module.
  4. Define members for types (classes) with calling convention, parameters, and access modifiers.
  5. Emit IL Opcodes for the application logic.

The code in this example is divided into two parts: a DLL of the IL code generator and an application that instantiates the code generating class and calls its member function. First, we will see the code generator DLL code, shown in Listing 6.

Listing 6

using System;
using System.Reflection;
using System.Reflection.Emit;   // To emit MSIL
using System.Threading;         // To get Current AppDomain

public class MSILGen
{
public MSILGen()
{
  // Create a simple name for the assembly.
  AssemblyName assemblyName = new AssemblyName();
  assemblyName.Name = "CodeGenAssembly";

  // Create the dynamic assembly.
  AppDomain appDomain = Thread.GetDomain();
  AssemblyBuilder assembly = appDomain.DefineDynamicAssembly
                            (assemblyName,
                             AssemblyBuilderAccess.Run);

  // Create a dynamic module.
  ModuleBuilder module = assembly.DefineDynamicModule
                                  ("CodeGenModule");


  // Define a public class named "CodeGenClass" in the assembly.
  TypeBuilder helloWorldClass = module.DefineType("CodeGenClass",
                                TypeAttributes.Public);

  // Define a private String field named "Message" in the type.
  FieldBuilder greetingField = helloWorldClass.DefineField
                               ("Message", typeof(String),
                               FieldAttributes.Private);

  // Create the constructor.
  Type[] constructorArgs = { typeof(String) };
  ConstructorBuilder constructor = helloWorldClass.
                                   DefineConstructor(
                                   MethodAttributes.Public,
                                   CallingConventions.Standard,
                                   constructorArgs);

  // Generate IL for the method. The constructor calls its
  // superclass constructor. The constructor stores its argument
  // in the private field.
  ILGenerator constructorIL = constructor.GetILGenerator();
  constructorIL.Emit(OpCodes.Ldarg_0);

  ConstructorInfo superConstructor = typeof(Object).
                  GetConstructor(new Type[0]);

  constructorIL.Emit(OpCodes.Call, superConstructor);
  constructorIL.Emit(OpCodes.Ldarg_0);
  constructorIL.Emit(OpCodes.Ldarg_1);
  constructorIL.Emit(OpCodes.Stfld, greetingField);
  constructorIL.Emit(OpCodes.Ret);

  // Create the GetMessage method.
  MethodBuilder getGreetingMethod = helloWorldClass.DefineMethod(
                                    "GetMessage",
                                    MethodAttributes.Public,
                                    typeof(String), null);

  // Generate IL for GetGreeting.
  ILGenerator methodIL = getGreetingMethod.GetILGenerator();
  methodIL.Emit(OpCodes.Ldarg_0);
  methodIL.Emit(OpCodes.Ldfld, greetingField);
  methodIL.Emit(OpCodes.Ret);

  // Fry the class CodeGenClass.
  typ = helloWorldClass.CreateType();
}

  Type typ;

  public Type T
  {
    get { returm this.typ; }
  }
}

Let's see some description about the code. In the using section, we included the System.Reflection.Emit namespace. This namespace contains classes that allow the compiler/tools to produce (emit) metadata and Intermediate Language (MSIL). And the System.Threading namespace is used to get the current AppDomain. An AppDomain is a logical grouping of assemblies. This is something similar to the Win32 processes. We just code the steps explained above. First, we create a new AssemblyName object. This class describes the assembly's unique identity. AssemblyName objects are used to bind and retrieve information about an assembly. We need to give a name to identify the assembly; here, it's "CodeGenAssembly". Once we have an initialized assembly name and current domainn, we can create the assembly by using the appDomain.DefineDynamicAssembly method. Note that we need to pass two parameters to this function:

Thread.GetDomain and the mode of access AssemblyBuilderAccess.Run tell that the assembly can be executed and not saved in memory. The DefineDynamicAssembly method returns an AssemblyBuilder object that we then cast to an Assembly object. At this point, we have a fully functional assembly in memory. Now, we need to create its temporary module and that module's type.

Next, create a module dynamically by using the AssemblyBuilder.DefineDynamicModule method by specifying the module name. Here, it's "CodeGenModule", which returns a ModuleBuilder object. The DefineDynamicModule method returns an object of ModuleBuilder. Once we have ModuleBuilder, call its DefineType method to create a new type (class), namely the "CodeGenClass" and specifying the access modifier attributes TypeAttributes.Public. This method returns the TypeBuilder object. Once we have the TypeBuilder object, we can create any type of member we want by using TypeBuilder.DefineMember, a field by using TypeBuilder.DefineField, and a constructor by using TypeBuilder.DefineConstructor. Note that a private string field "message" is defined in the class to store the message that goes from the client. We have defined a constructor for our class with a string argument using TypeBuilder.DefineConstructor.

Now, all we do is decide what code to place in this method. To do this, the code instantiates an ILGenerator object by using the ConstructorBuilder.GetILGenerator method and calls the different ILGenerator methods to write MSIL code into the method. Generate IL for the method. The constructor calls its superclass constructor. The constructor stores its argument in the private field.

Now, we create a "GetMessage" method by calling TypeBuilder.DefineMethod; this method returns a MethodBuilder object. Get the ILGenerator of this object and emit MSIL code for this method. Finally, call the CreateType of the class; this should always be the last step performed after you've defined the members for a new type. The Emit function emits the opcodes to the MSIL stream to the Just-In-Time compiler. The operation that we want to emit is specified by the OpCodes class's members, which are passed as parameters. Once all the opcode work is finished, we need to call OpCodes.Ret, which returns from the current method. Then, we can take this Type in the client. That's it.... We have completed the code generating part.

Build the DLL by using the following command line switch:

csc /t:library MSILGen.cs

Now, we develop a console client application to use this code generator. Listing 7 shows the code for this.

Don't forget to refer MSILGen.dll to this application. First, create a new instance of the MSILGen class (Type). Create the MSILGen type dynamically by using the Activator.CreateInstance member. Note that we pass a string to the constructor argument to construct the object and the type. Great; now we have an activated object in memory. Just call tje Type class's InvokeMember.... Mission done!!! We called the member.

Listing 7

using System;
using System.Reflection;
using System.Reflection.Emit;

class Class1
{
static void Main()
  {
    MSILGen codeGen = new MSILGen();

    Type typ = codeGen.T;

    // Create an instance of the "CodeGenClass" class.
    object codegen = Activator.CreateInstance(typ, new object[]
{"Hallo from code Generator."});

    // Invoke the "GetMessage" method of the "CodeGenClass" class.
object obj = typ.InvokeMember("GetMessage", 
BindingFlags.InvokeMethod, null, codegen, null);

    Console.WriteLine("CodeGenClass.GetMessage
                       returned: \"" + obj + "\"");
  }
}

Downloads

Download demo files - 43 Kb


Comments

  • Qué colecciones con secador de pelo GHD alisadores

    Posted by hanmeihm on 05/30/2013 12:25pm

    [url=http://www.comprarsaleghd.webstarts.com/]planchas ghd baratas[/url] Eso es slo parte de la historia, sin embargo. En todo ese mismo momento, Jennifer Aniston haba sido un tatuaje de moda importante. Su novia haba tenido en su ex cabello en una forma rpida, deslumbrante, al instante robert y ?sabes qu?, Su amor acaricia planchas ghd. A continuacin celeb har con prontitud y tambin ghd lleg a ser uno de los secretos mejor mantenidos hasta llegar parece ser que de hecho haba sido ya previamente bsicamente concebible a travs de su estilista. [url=http://www.planchaspeloghdes.qsite.dk/]planchas ghd baratas[/url] Clubes de pelo GHD vienen con varias temperaturas para que se puedan tomar para obtener una amplia gama de diversos diseos de pelo. Sus clubes comen casi nada de energa, son realmente ecolgicos y seguros para su uso. La unidad de reajuste autom¢tico asegura que las cerraduras no se debilitan con slo conseguir demasiado caliente. [url=http://www.planchaspeloghdes.qsite.dk/]planchas ghd baratas[/url] Es evidente que la ma es rizado, muy rizado clasificacin. Detiene a 4 o C, pero creo que la mina debe ser un 4Z o quiz¢s un cinco.

    Reply
  • Typically the nike Sector Call - And so, who cares for nothing wins?!?

    Posted by BobHotgloff on 05/03/2013 04:16am

    Brief blog post demonstrates to you the cogs and wheels of the nike and consequently the actions one need to do right now. [url=http://www.mizunogoruhujp.com/]ミズノ[/url] The Secret tips For nike [url=http://www.mizunogoruhujp.com/ミズノ-ゴルフクラブ-c-1.html]ミズノ アイアン[/url] Extra short study instructs you all cogs and wheels of the nike and what you ought to do right away. [url=http://www.mizunogoruhujp.com/ゴルフグローブ-c-33.html]ミズノ グローブ[/url] The thing all others does in regard to mizuno and consequently those actions youwant to do completely different. [url=http://www.mizunogoruhujp.com/ゴルフバッグ-c-7.html]ミズノゴルフ[/url] Neutral blog post unveil Six brand new things for nike that noone is discussing. [url=http://www.mizunogoruhu.com/]mizuno golf[/url] The ideal double twirl on nike [url=http://www.mizunogoruhu.com/ミズノmizuno-クラブ-c-4.html]ミズノ グラブ[/url] Items and formation throughout Nevada - - mizuno actually leaves with no good-bye [url=http://www.mizunogoruhu.com/ミズノmizuno-アイアン-c-3.html]ミズノ[/url] Highly effective strategies of mizuno which you can use beginning immediately. [url=http://www.mizunogoruhu.com/ミズノmizuno-バッグ-c-5.html]ミズノ[/url] Short publishing reveal the incontestable information regarding nike and exactly how it might have an effect your company.

    Reply
  • Latebinding

    Posted by Pramod Kumar Sah on 03/21/2013 03:18am

    //Late Binding:---------- using System; class parent { public parent() { } virtual public void show() { Console.WriteLine("From a"); } public static void Main() { parent p=new child(); //p has not reference of child but when i apply virtual than p can have reference of child p.show(); } } class child: parent { public child() { } override public void show() { Console.WriteLine("From b"); } }

    Reply
  • Latebinding

    Posted by Pramod Kumar Sah on 03/21/2013 03:16am

    // Late Binding:- using System; class parent { public parent() { } virtual public void show() { Console.WriteLine("From a"); } public static void Main() { parent p=new child(); //p has not reference of child but when i apply virtual than p can have reference of child p.show(); } } class child: parent { public child() { } override public void show() { Console.WriteLine("From b"); } }

    Reply
  • Interesting

    Posted by snareenactina on 11/16/2012 03:11am

    The impact of a rising population is to continue to put upward pressure on nominal GDP even if it means not much change in per capita GDP. Which also continues to feed the UK Inflation Mega-Trend. magnetism During the 1960s Chairman Mao told farmers to kill crop-eating sparrows, an edict which produced a plague of the insects which the sparrows normally ate. Likewise, the Digital Economy Bill, in trying to support artists' copyright and tackle illegal file-sharing, is about to produce a new culture – in which ISPs and bewildered householders are deluged with threatening legal letters from the entertainment industry. noocha overhang rudd pink predicate

    Reply
  • Need help

    Posted by Legacy on 10/13/2003 12:00am

    Originally posted by: Murugaraj

    How to pass array parameter and get(return) array from methods in Reflection?
    

    Reply
  • Excellent!

    Posted by Legacy on 09/12/2003 12:00am

    Originally posted by: Terr

    Exactly what I was looking for. Thank you! I laughed out loud when I saw the late binding example. It is virtually identical to what I'm trying to do.

    Reply
  • Need some help !

    Posted by Legacy on 04/01/2003 12:00am

    Originally posted by: Amit D. Desai

    Every thing is fine up-to point where we create method which has no parameters, but what if we want to pass parameters to method and/or i expect method to return something say for example:
    
    

    int MyAddMethod(int,int)

    Please help me solving this problem.

    Waiting for some good stuff.

    Thank you.

    Reply
  • I want to be a prof. in c#

    Posted by Legacy on 12/12/2002 12:00am

    Originally posted by: ramin

    may you help me?

    how can i lern c#?
    where is the best way,help,...?

    thanks a lot

    Reply
  • Some doubts - probably mindsets

    Posted by Legacy on 12/08/2002 12:00am

    Originally posted by: Alok Govil

    I seems to be having some doubts here, which are probably coming from some mindsets I am struck with.

    ----------

    Not talking in terms of C#, but in terms of core-concepts, generating code on-the-fly does NOT require reflection (obtaining metadata of the code-generator part, or of code-generated part). For example, compilers themselves generate code and call it (and they have been doing that from much before reflection was invented).

    ---------

    You said: The compiler knows nothing about these calls during compile time all are done during run time, "late binding."

    This is another thing I am confused about. Compiler does not know the value of the function pointer, but may or may not know about the "function declaration" of the functions in advance. (If it does not know about the function declaration, how will the later part of the code user that function?!)

    ---------

    As you mentioned, late binding could be achieved earlier also with LoadLibrary, just that reflection is a more elegant (and powerful) way of doing it. Am I correct?

    Thanks - Alok

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: November 20, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Are you wanting to target two or more platforms such as iOS, Android, and/or Windows? You are not alone. 90% of enterprises today are targeting two or more platforms. Attend this eSeminar to discover how mobile app developers can rely on one IDE to create applications across platforms and approaches (web, native, and/or hybrid), saving time, money, and effort and introducing apps to market faster. You'll learn the trade-offs for gaining long …

  • On-demand Event Event Date: October 23, 2014 Despite the current "virtualize everything" mentality, there are advantages to utilizing physical hardware for certain tasks. This is especially true for backups. In many cases, it is clearly in an organization's best interest to make use of physical, purpose-built backup appliances rather than relying on virtual backup software (VBA - Virtual Backup Appliances). Join us for this webcast to learn why physical appliances are preferable to virtual backup appliances, …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds