Reflection and Reflection.Emit in VB.NET Part 2
In this article I will explain you about Reflection and Reflection.Emit in VB.NET.
Read Part 1
Here, some more discussion with an example of Reflection and Reflection.Emit. You should use Reflection.Emit members for the following reasons:
-
You have your own macro languages, compilers, or script compilers in your applications.
-
You want to improve performance of your algorithms by creating assemblies, classes, modules, and new types during runtime.
-
You want to improve performance of late-bound objects. You can emit the code necessary to call bound types directly, and then call through your emitted method. Although you cannot perform calls as speedily as with early binding, you will perform better than late binding.
The System.Reflection.Emit namespace provides the classes necessary for a user to create an .exe file on the fly. Its classes allow a compiler or tool to emit metadata and MSIL. So you can create .exe files on your disk on the fly as if you were running the code, saving it, and calling the compiler to compile the code. Mostly you will need this feature and this namespace for your custom script engines and compilers.
The Reflection.Emit namespace has many members you can use for emitting. Here are the two most important ones:
-
The AssemblyBuilder class is the starting point for any application that emits code at runtime and has methods for creating dynamic modules.
-
The ModuleBuilder class is used as the starting point for adding types such as classes and structures to a dynamic assembly at runtime.
There is a more sophisticated example that shows how to emit an assembly that contains a method to calculate a factorial on the fly.
Example of Calculating Factorial by Emitting Operation Codes (emitfactorial.vb)
Imports System.Reflection
Imports System.Reflection.Emit
Imports System.IO
Imports System.Threading
Imports System.Diagnostics
' declare the interface
Public Interface IFactorial
Function myfactorial() As Integer
End Interface
Public Class SampleFactorialFromEmission
' emit the assembly using op codes
Private Function EmitAssembly(ByVal theValue As Integer) As Assembly
' create assembly name
Dim assemblyName As New AssemblyName()
assemblyName.Name = "FactorialAssembly"
' create assembly with one module
Dim newAssembly As AssemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run)
Dim newModule As ModuleBuilder = newAssembly.DefineDynamicModule("MFactorial")
' define a public class named "CFactorial" in the assembly
Dim myType As TypeBuilder = newModule.DefineType("CFactorial", TypeAttributes.[Public])
' Mark the class as implementing IFactorial.
myType.AddInterfaceImplementation(GetType(IFactorial))
' define myfactorial method by passing an array that defines
' the types of the parameters, the type of the return type,
' the name of the method, and the method attributes.
Dim paramTypes As Type() = New Type(-1) {}
Dim returnType As Type = GetType(Int32)
Dim simpleMethod As MethodBuilder = myType.DefineMethod("myfactorial", MethodAttributes.[Public]_
Or MethodAttributes.Virtual, returnType, paramTypes)
' obtain an ILGenerator to emit the IL
Dim generator As ILGenerator = simpleMethod.GetILGenerator()
' Ldc_I4 pushes a supplied value of type int32
' onto the evaluation stack as an int32.
' push 1 onto the evaluation stack.
' foreach i less than theValue,
' push i onto the stack as a constant
' multiply the two values at the top of the stack.
' The result multiplication is pushed onto the evaluation
' stack.
generator.Emit(OpCodes.Ldc_I4, 1)
For i As Int32 = 1 To theValue
generator.Emit(OpCodes.Ldc_I4, i)
generator.Emit(OpCodes.Mul)
Next
' emit the return value on the top of the evaluation stack.
' Ret returns from method, possibly returning a value.
generator.Emit(OpCodes.Ret)
' encapsulate information about the method and
' provide access to the method metadata
Dim factorialInfo As MethodInfo = GetType(IFactorial).GetMethod("myfactorial")
' specify the method implementation.
' pass in the MethodBuilder that was returned
' by calling DefineMethod and the methodInfo just created
myType.DefineMethodOverride(simpleMethod, factorialInfo)
' create the type and return new on-the-fly assembly
myType.CreateType()
Return newAssembly
End Function
' check if the interface is null, generate assembly
' otherwise it is already there, where it is to be...
Public Function DoFactorial(ByVal theValue As Integer) As Double
If thesample Is Nothing Then
GenerateCode(theValue)
End If
' call the method through the interface
Return (thesample.myfactorial())
End Function
' emit the assembly, create an instance and
' get the interface IFactorial
Public Sub GenerateCode(ByVal theValue As Integer)
Dim theAssembly As Assembly = EmitAssembly(theValue)
thesample = DirectCast(theAssembly.CreateInstance("CFactorial"), IFactorial)
End Sub
' private member data
Private thesample As IFactorial = Nothing
End Class
Class Class1
<STAThread()> _
Shared Sub Main(ByVal args As String())
Dim aValue As Int32 = 5
Dim t As New SampleFactorialFromEmission()
Dim result As Double = t.DoFactorial(aValue)
Console.WriteLine("Factorial of " & aValue & " is " & result)
Console.ReadLine()
End Sub
End Class
Output
Conclusion
Hope this article would have helped you in understanding the Reflection and Reflection.Emit in VB.NET.