Dynamic Invocation with Reflection Emit

So far you've created an assembly on the fly by writing its source code to disk and then compiling that source code. You then dynamically invoked the method you wanted to use from that assembly, which was compiled on disk. That brings a lot of overhead, and what have you accomplished? When you're done with writing the file to disk, you have source code you can compile, and when you're done compiling, you have IL (Intermediate Language) op codes on disk you can ask the .NET Framework to run.

Reflection emit allows you to skip a few steps and just "emit" the op codes directly. This is writing assembly code directly from your C# program and then invoking the result. It just doesn't get any cooler than that.

You start much as you did in the previous examples. You create a constant for the number to add to (200) and the number of iterations (1,000,000). You then re-create the myMath class as a benchmark.

Once again you have a ReflectionTest class, and once again you call DoSum, passing in the value:

As you can see, you will use an interface again, but this time you are not going to write a file to disk.

GenerateCode is quite different now. You no longer write the file to disk and compile it; instead you call the helper method EmitAssembly and get back an assembly. You then create an instance from that assembly and cast that instance to your interface.

An AssemblyName is an object that fully describes an assembly's unique identity. As discussed in Chapter 13, an assembly's identity consists of a simple name (DoSumAssembly), a version number, a cryptographic key pair, and a supported culture.

With this object in hand, you can create a new AssemblyBuilder object. To do so, you call DefineDynamicAssembly on the current domain, which you get by calling the static GetDomain( ) method of the Thread object. Domains are discussed in detail in Chapter 19.

The parameters to the GetDomain( )method are the AssemblyName object you just created and an AssemblyBuilderAccess enumeration value (one of Run, RunandSave, or Save). You'll use Run in this case to indicate that the assembly can be run but not saved:

With this newly created AssemblyBuilder object, you are ready to create a ModuleBuilder object. The job of the ModuleBuilder, not surprisingly, is to build a module dynamically. Modules are discussed in Chapter 17. You call the DefineDynamicModule method, passing in the name of the method you want to create:

ModuleBuilder newModule =
newAssembly.DefineDynamicModule("Sum");

Now, given that module, you can define a public class and get back a TypeBuilder object. TypeBuilder is the root class used to control the dynamic creation of classes. With a TypeBuilder object, you can define classes and add methods and fields:

You pass in the name of the method, the flags you want (public and virtual), the return type (int), and the paramTypes (the zero length array).

You then use the MethodBuilder object you created to get an ILGenerator object:

ILGenerator generator = simpleMethod.GetILGenerator( );

With your precious ILGenerator object in hand, you are ready to emit the op codes. These are the very op codes that the C# compiler would have created. (In fact, the best way to get the op codes is to write a small C# program, compile it, and then examine the op codes in ILDasm!)

First emit the value 0 to the stack. Then loop through the number values you want to add (1 through 200), adding each to the stack in turn, adding the previous sum to the new number and leaving the result on the stack:

Now you must specify the implementation that will implement the method. You call DefineMethodOverride on the TypeBuilder object you created earlier, passing in the MethodBuilder you created, along with the MethodInfo object you just created:

myType.DefineMethodOverride(simpleMethod, computeSumInfo);

You're just about done; create the class and return the assembly:

myType.CreateType( );
return newAssembly;

OK, I didn't say it was easy, but it is really cool, and the resulting code runs very fast. The normal loop runs 1,000,000 iterations in 11.5 seconds, but the emitted code runs in .4 second! A full 3,000% faster. Example 18-11 is the full source code.

Example 18-11: Dynamic invocation with reflection emit

namespace Programming_CSharp
{
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Threading;

// Define a method on the type to call. Pass an
// array that defines the types of the parameters,
// the type of the return type, the name of the
// method, and the method attributes.
Type[] paramTypes = new Type[0];
Type returnType = typeof(int);
MethodBuilder simpleMethod =
myType.DefineMethod(
"ComputeSum",
MethodAttributes.Public |
MethodAttributes.Virtual,
returnType,
paramTypes);

// Get an ILGenerator. This is used
// to emit the IL that you want.
ILGenerator generator =
simpleMethod.GetILGenerator( );

// Emit the IL that you'd get if you
// compiled the code example
// and then ran ILDasm on the output.

// Push zero onto the stack. For each 'i'
// less than 'theValue',
// push 'i' onto the stack as a constant
// add the two values at the top of the stack.
// The sum is left on the stack.
generator.Emit(OpCodes.Ldc_I4, 0);
for (int i = 1; i <= theValue;i++)
{
generator.Emit(OpCodes.Ldc_I4, i);
generator.Emit(OpCodes.Add);

}

// return the value
generator.Emit(OpCodes.Ret);

//Encapsulate information about the method and
//provide access to the method's metadata
MethodInfo computeSumInfo =
typeof(IComputer).GetMethod("ComputeSum");

// specify the method implementation.
// Pass in the MethodBuilder that was returned
// by calling DefineMethod and the methodInfo
// just created
myType.DefineMethodOverride(simpleMethod, computeSumInfo);

Reflection emit is a powerful technique for emitting op codes. Although today's compilers are very fast and today's machines have lots of memory and processing speed, it is comforting to know that when you must, you can get right down to the virtual metal.