Execution-Time Code Generation in C#: Lightweight Code Generation

An assembly can’t be unloaded from an application domain once it has been loaded. This means the only way to unload an assembly is to unload the entire application domain, which generally means if an assembly needs to be unloaded without terminating a process, a secondary application domain needs to be created just to hold the assemblies that need to be unloaded. In the examples presented in this chapter so far, the dynamically created assemblies are loaded into the main application domain and hence can’t be unloaded.

In some situations, dynamically generated code is quite small in nature, and the requirement of generating a full assembly and then housing this in a secondary application domain just so it can be unloaded feels like overkill. In recognition of this, .NET 2.0 introduces a lightweight code generation model that allows the generation of static methods without a full assembly, without the requirement to undergo verification (security permissions permissible), and with the ability to be reclaimed without unloading an application domain.

Because the methods generated by lightweight code generation must be static, they couldn’t be used to implement the polynomial examples presented earlier in this chapter that relied on deriving from the abstract Polynomial class; however, by removing the polymorphic and instance calls, it’s possible to solve polynomials with lightweight code generation:

using System;

using System.Reflection;

using System.Reflection.Emit;

namespace Polynomial {

class LightWeightPoly {

public void Eval()

{

// Evaluate the first polynomial, with 7 elements

double[] coeff = new double[] { 5.5, 7.0, 15, 30, 500, 100, 1 };

DynamicMethod dm = GetEvaluator(coeff);

object[] parameter = new object[] { 2.0 };

double result = (double)dm.Invoke(null, parameter);

}

DynamicMethod GetEvaluator(params double[] coefficients)

{

//define dynamic method contruction data

Type[] paramTypes = new Type[] { typeof(double) };

Type returnType = typeof(double);

Type methodOwner = this.GetType();

//

//create dynamic method

DynamicMethod dm = new DynamicMethod(“Evaluate”, returnType, paramTypes,

methodOwner, false);

ILGenerator il = dm.GetILGenerator();

//

// Emit the IL. This is a hand-coded version of what

// you’d get if you compiled the code example and then ran

// ILDASM on the output.

//

//

// This first section repeated loads the coefficient’s

// x value on the stack for evaluation.

//

for (int index = 0; index < coefficients.Length – 1; index++)

{

il.Emit(OpCodes.Ldc_R8, coefficients[index]);

il.Emit(OpCodes.Ldarg_1);

}

// load the last coefficient

il.Emit(OpCodes.Ldc_R8, coefficients[coefficients.Length – 1]);

// Emit the remainder of the code. This is a repeated

// section of multiplying the terms together and

// accumulating them.

for (int loop = 0; loop < coefficients.Length – 1; loop++) {

il.Emit(OpCodes.Mul);

il.Emit(OpCodes.Add);

}

// return the value

il.Emit(OpCodes.Ret);

//dynamic method now done – return it

return dm;

}

}

}

Generating C# code and compiling it or using Reflection.Emit are valid techniques, but they should probably be the last resort in your performance improvement arsenal. The simple example in this chapter is much easier to write, debug, and maintain.

Techniques such as these are already used in the .NET Framework; the regular expression class in the System.Text.RegularExpressions namespace uses Reflection.Emit to generate a custom-matching engine when a regular expression is compiled.

Source: Gunnerson Eric, Wienholt Nick (2005), A Programmer’s Introduction to C# 2.0, Apress; 3rd edition.

Leave a Reply

Your email address will not be published. Required fields are marked *