WEBINAR:On-Demand

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.

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.

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:

Define an assembly in memory.

Create a module for an assembly.

Define types (classes) for a module.

Define members for types (classes) with calling convention, parameters, and access modifiers.

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.

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.

Some doubts - probably mindsets

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?

CodeDOM is much easier to use.

This article shows perfectly valid way to generate dynamic assembly, but from my experience, if you aren't native MSIL speaker and you need to generate something complex (automatc DB access,remoting proxy, etc) it is much easier to use CodeDOM classes. In addition you can save generated source code and debug generated program.
But "Emit" technique is perfect where you generate code based on some algorthm (i.e. Regexp).

Advertiser Disclosure:
Some of the products that appear on this site are from companies from which QuinStreet receives compensation. This compensation may impact how and where products appear on this site including, for example, the order in which they appear. QuinStreet does not include all companies or all types of products available in the marketplace.

Thanks for your registration, follow us on our social networks to keep up-to-date