Emitting Types at Runtime Using Reflection


Visual Basic .NET Unleashed
By Paul Kimmel
Table of Contents
Chapter 6.  Reflection

Emitting Types at Runtime Using Reflection

The System.Reflection.Emit namespace allows developers to emit types at runtime.

Part of the interest in writing books like this one is to try to understand what motivated Microsoft developers when they defined and implemented these capabilities. Sometimes it is obvious or guessable, and other times I have to ask and am fortunate enough to get answers from those in the know.

You are likely to use some of these new capabilities as the implementers intended, and more than likely many of you will contrive new uses for them. Reflection seems to be a replacement for COM Automation. The Emit namespace seems to be intended for code generators, perhaps for tools like Rational Rose that reverse-engineer UML models to generate code.

The Reflection.Emit namespace supports generating types at runtime using Builder classes. These Builder classeslike AssemblyBuilder and MethodBuilderwork by emitting IL (Intermediate Language) code. General information about classes capable of emitting code to the ILGenerator can be found by searching the help documentation topic ms-help://MS.VSCC/MS.MSDNVS/cpref/html/frlrfSystemReflectionEmit.htm in Visual Studio .NET.

There are many excellent examples of .NET code defined in the Visual Basic QuickStarts and Other Samples help topics. If you search the QuickStarts topics, you will find several solutions demonstrating various aspects of Visual Basic .NET programming. The example in Listing 6.6 is loosely based on the ReflectionEmitVB.vbproj example installed in the Microsoft.Net\FrameworkSDK folder when you installed the .NET Framework.

Listing 6.6 Emitting a dynamic class as IL code
  1:  Option Strict On  2:  Option Explicit On  3:   4:  Imports System.Threading  5:  Imports System.Reflection  6:  Imports System.Reflection.Emit  7:   8:  Public Class Form1  9:  Inherits System.Windows.Forms.Form  10:   11:  [ Windows Form Designer generated code ]  12:   13:  Private Sub Button1_Click(ByVal sender As System.Object, _  14:  ByVal e As System.EventArgs) Handles Button1.Click  15:   16:  DynamicType.Test()  17:  End Sub  18:   19:  End Class  20:   21:   22:  Public Class DynamicType  23:   24:  Public Shared Sub Test()  25:  Dim AClass As Type = _  26:  CreateType(Thread.GetDomain, AssemblyBuilderAccess.Run)  27:   28:  Dim Obj As Object = _  29:  Activator.CreateInstance(AClass, _  30:  New Object() {"It's a brave new world!"})  31:  ' Reuse Obj reference here!  32:  Obj = AClass.InvokeMember("Text", _  33:  BindingFlags.GetField, Nothing, Obj, Nothing)  34:   35:  MsgBox(Obj)  36:  End Sub  37:   38:  Public Shared Function CreateType(ByVal Domain As AppDomain, _  39:  ByVal Access As AssemblyBuilderAccess) As Type  40:   41:  Dim AName As New AssemblyName()  42:  AName.Name = "EmitAssembly"  43:  Dim AnAssembly As AssemblyBuilder = _  44:  Domain.DefineDynamicAssembly(AName, Access)  45:   46:  Dim AModule As ModuleBuilder  47:  AModule = AnAssembly.DefineDynamicModule("EmitModule")  48:   49:  Dim AClass As TypeBuilder = _  50:  AModule.DefineType("AClass", TypeAttributes.Public)  51:   52:  Dim AField As FieldBuilder = _  53:  AClass.DefineField("Text", GetType(String), _  54:  FieldAttributes.Public)  55:   56:  Dim Args As Type() = {GetType(String)}  57:  Dim Constructor As ConstructorBuilder = _  58:  AClass.DefineConstructor(MethodAttributes.Public, _  59:  CallingConventions.Standard, Args)  60:   61:  Dim IL As ILGenerator = Constructor.GetILGenerator  62:  IL.Emit(OpCodes.Ldarg_0)  63:  Dim Super As ConstructorInfo = _  64:  GetType(Object).GetConstructor(Type.EmptyTypes)  65:  IL.Emit(OpCodes.Call, Super)  66:  IL.Emit(OpCodes.Ldarg_0)  67:  IL.Emit(OpCodes.Ldarg_1)  68:  IL.Emit(OpCodes.Stfld, AField)  69:  IL.Emit(OpCodes.Ret)  70:   71:  Return AClass.CreateType  72:   73:  End Function  74:   75:  End Class 

The shared method Test is called, which starts the process of creating the dynamic type. CreateType returns a Type object. Activator.CreateInstance is used directly to create an instance of the new type on line 28. (We used the Activator object implicitly in Listing 6.3 when Type.CreateInstance was called.) The Obj reference is reused on line 32 when we call AClass.InvokeMember to get the value of the field named Text. After lines 3233 Obj refers to the String member Text. Clearly this is not an efficient way to write code in general, but is a powerful way to create types on the fly.

The CreateType method may seem a bit confusing to less experienced developers. After you have read the first 10 chapters or so of this book, the code itself should be comprehensible. Clearly, emitting IL is an advanced subject.

In summary, CreateType uses builder classes to create the various pieces of code. An AssemblyName and AssemblyBuilder are created first to create an assembly. On lines 46 and 47 a Module is created, using the ModuleBuilder class, and added to the assembly. Lines 49 and 50 define a single statement that creates the new class type. The statements on lines 52 to 54 add a single public, string field to the type. (Thus far, these are all steps that a programmer would perform manually by creating a project and defining a class in that assembly.) The ConstructorBuilder is used to define a parameterized constructor for our new type; the new constructor takes a single string argument. Lines 61 through 69 use an ILGenerator object to emit the IL code. Line 71 returns the new type.

Lines 65 through 69 represent generic, low-level code in IL that will be converted to machine-specific code by the JITter. These five strange -looking statements using OpCodes represent the IL form of code that defines a constructor. Line 65 calls the base constructor. Lines 66 and 67 manage arguments passed to the constructor, including the hidden reference to self, Me and the string parameter. Line 68 initializes the field, and line 69 emits the equivalent of the Ret instruction. Basically, the code is the IL version of statements you might write: MyBase.New and Text = Value.

Emitting code using reflection is one of those subjects that will probably be explored in its own book, after the dust from absorbing the core changes to Visual Basic .NET.


Visual BasicR. NET Unleashed
Visual BasicR. NET Unleashed
Year: 2001
Pages: 222

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net