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)
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 '...
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