Reflection Part II: Emit

irst, a quick review of exactly what reflection is and what it is used for. From Part 1, you know that “Reflection is a means of discovering information about objects at runtime and executing against those objects.” This functionality is provided in .NET by the System.Reflection namespace. Classes in this namespace, including Assembly, Module, ConstructorInfo, MethodInfo, and others, are used for type discovery and dynamic invocation. Put simply, they allow you to both explore the classes, methods, properties, and fields (types) exposed by an assembly as well as create an instance of a type and execute one of its methods (invoke members). While these features are great for run-time object discovery, reflection in .NET does not stop there. Reflection also allows you to build assemblies and create entirely new types at run-time. This is known as reflection emit.

What Is Reflection Emit?
Nested under System.Reflection is the System.Reflection.Emit namespace, which is home for all of the Framework classes that allow you to dynamically build assemblies and types from scratch. Although the very act of generating code on demand is likely a feature that few developers will ever need, it is a credit to the .NET Framework that the tools are there, waiting for the right business problem to solve.

As an example, using reflection emit it would be possible to:

  1. Create a new assembly. (Assemblies can be “dynamic” in that they exist only in memory, or can be persisted to disk.)
  2. Within the assembly, create a module.
  3. Within the module, create a type.
  4. Add properties and methods to that type.
  5. Generate the code inside of the method or property.

As it turns out this is actually the exact process you will follow when you use the Reflection.Emit classes to generate code.

(See Sidebar: Constructors)
(See Sidebar: ILDASM)
(See Sidebar: Generating Executables)

Code Generation Process
Following the steps listed above, let’s examine the exact operations that are necessary to build out an assembly. For the purposes of this very basic example, let’s assume that you want to build a class called MathOps that has one public method (a function). This function will accept two input parameters as integers and return the sum of those two integers.

Step 1: Building the Assembly.
To expand slightly on the steps listed above, the actual process for step 1 looks like this:

  1. Create an AssemblyName. (This is used to uniquely identify and name the assembly.)
  2. Grab a reference to the current application domain. (The app domain will provide the actual method that returns an AssemblyBuilder object.)
  3. Create an AssemblyBuilder instance by calling AppDomain.DefineDynamicAssembly.

To start the assembly generation process you first need to create an AssemblyName instance that you’ll use to identify your assembly.

   ' Create a name for the assembly.   Dim name As New AssemblyName()   name.Name = "MyAssembly"

Next, you need an instance of a System.AppDomain class. You can get this from the currently running (static) thread instance.

   Dim ad As AppDomain   ad = Thread.GetDomain()

With these two elements in place you can define an AssemblyBuilder class and create an instance of it using the AppDomain and AssemblyName that you previously created. The AssemblyBuilder class is the work-horse of reflection emit. It provides the primary mechanisms you need to create new assemblies from scratch. You also need to specify an AssemblyBuilderAccess enumeration value that will indicate whether you want the assembly written to disk, saved in memory, or both. In this example, you want to save the assembly in memory.

   Dim ab As AssemblyBuilder   ab = ad.DefineDynamicAssembly(name, _   AssemblyBuilderAccess.Run)

Step 2: Defining a Module.
In Step 2 you’ll use the ModuleBuilder class to create a dynamic module inside of the assembly that you previously created. ModuleBuilder creates modules inside of an assembly. Calling the DefineDynamicModule method of the AssemblyBuilder object will return an instance of a ModuleBuilder. As with the assembly, you must give the module a name (although here, the name is just a string).

   Dim mb As ModuleBuilder   mb = ab.DefineDynamicModule("MyModule")

Step 3: Creating a Type.
Now that you have an assembly and a module, you can add your class into the assembly. To build a new type (in your case you’re building a MyClass type), you use the TypeBuilder class. You’ll use a method from the “parent” object to actually return an instance of the builder object.

   Dim theClass As TypeBuilder   theClass = mb.DefineType("MathOps", _   TypeAttributes.Public)

Note that you’ve specified the visibility of the type as public by using the TypeAttributes enumeration.

Step 4: Adding a Method.
With the class type created, you can now add your method to the class. Call the method ReturnSum, and create it as a public function.

Use the MethodBuilder class to specify methods for a specific type. You can create a MethodBuilder instance by calling DefineMethod on your previously created type object. DefineMethod expects four parameters: the method, any attributes the method might possess (such as public, private, etc.), parameters, and a return type. Parameters and return type can be void values in the case of a subroutine. In your case you’re creating a function so you need to specify both the parameters and return type values.

To specify the return type, create a type object that holds a return type value (a System.Int32 value).

   Dim retType As Type   retType = GetType(System.Int32)

You’ll use an array of type values to specify the parameters to the function. Both of the parameters are int32 values as well.

   Dim parms As Type()   parms(0) = GetType(System.Int32)   parms(1) = GetType(System.Int32)

With these items in hand you can now call DefineMethod.

   Dim mb As MethodBuilder = _   theClass.DefineMethod("ReturnSum", _   methodAttributes.Public, retType, parms)

Step 5: Generating Code.
Since you have, in essence, stubbed out the function in step 4, you now need to add the actual code body to the method. This is really the core of the code generation process with reflection emit.

Note that reflection emit classes do not generate source code. In other words, your efforts here won’t create Visual Basic .NET or C# .NET code. Instead, your reflection emit classes will emit MSIL op codes.

It is important to note that reflection emit classes do not generate source code. In other words, your efforts here won’t create Visual Basic .NET or C# .NET code. Instead, your reflection emit classes will emit MSIL op codes. MSIL (Microsoft Intermediate Language) is an intermediate code language that closely resembles assembler. MSIL is consumed by the .NET JIT compiler when it creates native binaries. Op codes are low-level, assembler-like operating instructions.

Consider the following implementation of ReturnSum:

   Function ReturnSum(ByVal val1 As Integer, _   ByVal val2 As Integer) As Integer   Return val1 + val2   End Function

If you want to emit this code, you would first need to figure out how to write this function using only MSIL op codes. Thankfully, there is a quick and easy way to do this. You can simply compile the code and examine the resulting assembly using the .NET Framework ILDASM.exe utility. Compiling the function above yields the following MSIL version:

   .method public instance int32  ReturnSum(int32 val1,   int32 val2) cil managed   {   // Code size       9 (0x9)   .maxstack  2   .locals init ([0] int32 ReturnSum)   IL_0000:  nop   IL_0001:  ldarg.1   IL_0002:  ldarg.2   IL_0003:  add.ovf   IL_0004:  stloc.0   IL_0005:  br.s       IL_0007   IL_0007:  ldloc.0   IL_0008:  ret   } // end of method MathOps::ReturnSum

To emit this code you will use the ILGenerator class. You can retrieve an instance of the ILGenerator class specific to your method by calling the MethodBuilder.GetILGenerator() method. In other words, the following code will obtain an ILGenerator instance for your ReturnSum method.

   Dim gen As ILGenerator   gen = mb.GetILGenerator()

Using the generator object you can inject op code instructions to the method.

   gen.Emit(OpCodes.Ldarg_1)   gen.Emit(OpCodes.Ldarg_2)   gen.Emit(OpCodes.Add_Ovf)   gen.Emit(OpCodes.Stloc_0)   gen.Emit(OpCodes.Br_S)   gen.Emit(OpCodes.Ldloc_0)   gen.Emit(OpCodes.Ret)

At this point you’ve essentially finished creating the function, class, module, and assembly. To get a reference to this class you can make one call to CreateType like this:

   theClass.CreateType()

Namespaces and Classes
As you’ve already seen, the Reflection.Emit namespace contains a core set of “builder” classes that you use to create types and the various attributes, methods, fields, properties, and so on that are associated with the new type. Table 1 describes the primary classes you’ll use when emitting code with reflection.

Table 1: Reflection emit-related class reference.

Namespace.Class

System.Reflection.Emit.AssemblyBuilder

Primary Use

Defines a dynamic .NET assembly: A self-describing .NETbuilding block. A dynamic assembly is one that was specifically generatedthrough reflection emit. This class inherits fromSystem.Reflection.Assembly.

Example Call

Dim ab As AssemblyBuilder

Dim ad As AppDomain

ad = Thread.GetDomain()

ab = ad.DefineDynamicAssembly(name,AssemblyBuilderAccess.Run)

Namespace.Class

System.Reflection.Emit.ConstructorBuilder

Primary Use

Used to create and represent the constructor of a dynamicclass. It encapsulates all information related to the constructor including itsname, function signature, and code body. This is only needed if you are creatinga parameter-driven constructor or you’re overriding the default behavior of theparent class’s constructor.

Example Call

Dim ourClass As TypeBuilder = [module].DefineType(“ourClass”,_

TypeAttributes.Public)

?

Dim ctorArgs As Type() = {GetType(String)}

Dim ctor As ConstructorBuilder = _

ourClass.DefineConstructor(MethodAttributes.Public,_

CallingConventions.Standard,constructorArgs)

Namespace.Class

System.Reflection.Emit.CustomAttributeBuilder

Primary Use

Used to create custom attributes for dynamicclasses.

Namespace.Class

System.Reflection.Emit.EnumBuilder

Primary Use

Defines and represents anenumeration.

Namespace.Class

System.Reflection.Emit.EventBuilder

Primary Use

EventBuilder creates events for a dynamicclass.

Namespace.Class

System.Reflection.Emit.FieldBuilder

Primary Use

Use FieldBuilder to define fields on aclass.

Namespace.Class

System.Reflection.Emit.ILGenerator

Primary Use

Used to generate MSIL code (Microsoft IntermediateLanguage).

Example Call

Dim gen As ILGenerator =someMethod.GetILGenerator()

gen.Emit(OpCodes.Ldarg_0)

gen.Emit(OpCodes.Ret)

Namespace.Class

System.Reflection.Emit.LocalBuilder

Primary Use

Creates variables that are local to a given method orconstructor.

Namespace.Class

System.Reflection.Emit.MethodBuilder

Primary Use

Used to create and represent methods inside of aclass.

Example Call

Dim t as Type =myModule.GetType(“MyClassName”)

Namespace.Class

System.Reflection.Emit.MethodRental

Primary Use

A utility class used to swap a method into a dynamicallycreated class from some other class template. This is useful when you need toquickly re-create functionality via a method that already existselsewhere.

Namespace.Class

System.Reflection.Emit.ParameterBuilder

Primary Use

Creates parameters for use in a methodsignature.

Namespace.Class

System.Reflection.Emit.PropertyBuilder

Defines properties for a type.

Primary Use

Coupling Emit and Dynamic Invocation
Now that you know how to emit a dynamic assembly using the Reflection classes, let’s integrate the concept of dynamic invocation (covered in Part 1 of this article).

One example of when to use Reflection and Reflection.Emit is with code or script evaluation at run-time. It would be possible, for instance, to display a Windows form with a text box, ask the user to type in a formula, and then evaluate that formula at run-time through compiled code.

Another time to use Reflection.Emit classes is to optimize performance. Code solutions, by design, tend to be generalized solutions to a problem. This is often a good thing from a design perspective because it makes your systems flexible. For instance, if you want to compute the sum of numbers and you don’t necessarily know at design time how many values you need to sum, then you may need to call a loop. If you rewrite the ReturnSum function to accept an array of integers, you could loop through the members of that array, add each to a counter value, and then return the sum of all of the values. This is a nice generic solution since it doesn’t care about the total number of values contained in the array.

   Public Function ReturnSum(ByVal values() _   As Integer) As Int32   Dim i As Int32   Dim retValue As Int32   For i = 0 To values.GetUpperBound(0) - 1   retValue = retValue + values(i)   Next   Return retValue   End Function

On the other hand, if you hard-code the array limit you can return the sum in a more optimized fashion by writing one long math operation statement. For a few values or even hundreds of values the difference would be negligible. But, if you’re dealing with thousands or millions of values, the hard-coded method will be much, much faster. In fact, you can make this solution even faster by pulling out the array values and summing them with straight number addition while at the same time eliminating any ‘zero’ values that won’t affect the result anyway:

   Public Function ReturnSum() As Int32   Return 9 + 32 + 8 + 1 + 2 + 2 + 90 '...   End Function

The problem here, of course, is that you’ve written code that is not general and is not flexible.

So how do you get the best of both worlds? Answer: Reflection.Emit.

By combining Reflection.Emit functionality (such as taking in an array ceiling and array values, and then compiling the code), and Reflection functionality (such as locating, loading, and running the assembly that was emitted), you can craft some pretty ingenious performance solutions while avoiding brittle code. In this simple case you could write a loop that generates the MSIL op codes you need.

Consider the following console application that consumes an array and creates a new assembly, module, class, and ReturnSum method that directly sums the array values without resorting to a loop. The code in Listing 1 showcases many of the same concepts you’ve already seen in terms of using the emit “builder” objects. Please note some new code that is introduced with the application: Activator.CreateInstance is used to create an instance of the newly created object type, and the InvokeMember method is used to call the ReturnSum method on the type.

Reflection and Reflection.Emit namespaces allow programmers to dynamically generate and execute managed code at run-time. They provide a highly specialized set of classes for dealing with a uniquely specialized set of business problems.

You can download the code for this article at www.brilliantstorm.com/resources.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

The Latest

technology leadership

Why the World Needs More Technology Leadership

As a fact, technology has touched every single aspect of our lives. And there are some technology giants in today’s world which have been frequently opined to have a strong influence on recent overall technological influence. Moreover, those tech giants have popular technology leaders leading the companies toward achieving greatness.

iOS app development

The Future of iOS App Development: Trends to Watch

When it launched in 2008, the Apple App Store only had 500 apps available. By the first quarter of 2022, the store had about 2.18 million iOS-exclusive apps. Average monthly app releases for the platform reached 34,000 in the first half of 2022, indicating rapid growth in iOS app development.

microsoft careers

Top Careers at Microsoft

Microsoft has gained its position as one of the top companies in the world, and Microsoft careers are flourishing. This multinational company is efficiently developing popular software and computers with other consumer electronics. It is a dream come true for so many people to acquire a high paid, high-prestige job