This article has been
excerpted from book "The Complete Visual C# Programmer's Guide" from the Authors
of C# Corner.
The Reflection.Emit namespace classes can be used to emit Microsoft intermediate
language (MSIL) code on the fly so that the generated code can be executed
directly. Reflection is also used to obtain information about a class and its
members. In other words, reflection is a technology that allows you to examine
metadata that describes types and their members. You may have programmatically
accessed Component Object Model type libraries before; reflection in .NET is
very similar, but it is a lot more powerful and a lot easier to use. When you
compile a source file with a .NET compiler, the compiler emits the MSIL of the
statements within the source file, along with the metadata that describes the
types defined within the file. It is this metadata that the reflection APIs in
.NET enable you to examine. The MSIL and metadata are contained in assembly
files (typically .dll and .exe files).
The reflection API in .NET uses the System.Reflection namespace. In this
namespace are classes used to help you access structures inherent in a program
such as classes, types, fields, structs, enums, members, and methods. For
example, you use the Type class to identify the type of the class being
reflected, and the FieldInfo class represents the fields of a struct or enum.
The MemberInfo class represents the members of the reflected class, and you use
the MethodInfo class to represent methods of the reflected class. The
ParameterInfo class represents the parameters of a method in the reflected
class.
You can create code at runtime with the System.Reflection.Emit namespace
classes, but we must warn you that you must have some knowledge of the MSIL
operation codes and the MSIL language to take advantage of the emitting
commands. In fact, what you are actually emitting is the MSIL itself behind the
scenes. You can use reflection to define an assembly in memory, create a
class/module for that assembly, and then create other module members plus new
types for that module. You can construct the assembly using emitting.
Reflection.Emit is a powerful namespace in which we can dynamically emit
transient and persisting assemblies at runtime. Reflection.Emit produces a
low-level, language-neutral MSIL. Normally, we create an assembly by saving its
source code to disk and then compiling that source code. Then we call the
methods of classes that we need to use from that assembly, which was compiled on
disk. But as you can imagine, this involves extra disk write and read effort!
With reflection emit, we can omit this overhead and immediately emit the
operation codes directly into memory. Reflection emitting is nothing but writing
any assembly code directly within your code and then invoking the resulting code
on the fly.
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.
The ILGenerator.OpCodes class, which generates
MSIL instructions, includes all the MSIL operation codes in its fields that you
will need (operation codes are a portion of a set of operation descriptions that
specifies the operation to be performed or the set of operations in a computer).
MSIL is the typeless operation code of the base assembly language for the CLR or
intermediate language. When you code any C# code and compile it, it is
transformed into MSIL first. Then when you invoke an assembly in MSIL, it is
converted and executed in the corresponding machine language. The easiest way to
learn MSIL is to disassemble simple codes that you have compiled. You can
disassemble any compiled .NET code by using ILDasm.exe (the IL Disassembler),
one of the .NET SDK utilities. After you have compiled the code in Listing 21.28
and run it on the console, a new file is generated in your current folder called
TestAsm.exe. This .exe file prints the message "Hello World" on the console.
Listing 21.28: Creating an .exe File on the Fly (ReflectionEmit.cs)
using System;
using
System.Runtime;
using
System.Reflection;
using
System.Reflection.Emit;
public
class class1
{
public static
void Main()
{
AppDomain ad =
AppDomain.CurrentDomain;
AssemblyName am =
new AssemblyName();
am.Name = "TestAsm";
AssemblyBuilder ab =
ad.DefineDynamicAssembly(am, AssemblyBuilderAccess.Save);
ModuleBuilder mb =
ab.DefineDynamicModule("testmod",
"TestAsm.exe");
TypeBuilder tb = mb.DefineType("mytype",
TypeAttributes.Public);
MethodBuilder metb = tb.DefineMethod("hi",
MethodAttributes.Public |
MethodAttributes.Static,
null, null);
ab.SetEntryPoint(metb);
ILGenerator il = metb.GetILGenerator();
il.EmitWriteLine("Hello World");
il.Emit(OpCodes.Ret);
tb.CreateType();
ab.Save("TestAsm.exe");
}
}
Try executing TestAsm.exe, and see whether it works as you expect. Indeed, we
can accomplish anything by emitting operation codes that we can do with normal
C# code, since they both produce corresponding MSIL in the end. Listing 21.29 is
a more sophisticated example that shows how to emit an assembly that contains a
method to calculate a factorial on the fly.
Listing 21.29: Calculating Factorial by Emitting Operation Codes (emitfactorial.cs)
using System;
using
System.Reflection;
using
System.Reflection.Emit;
using System.IO;
using
System.Threading;
using
System.Diagnostics;
// declare the interface
public
interface IFactorial
{
int myfactorial();
}
public
class
SampleFactorialFromEmission
{
// emit the assembly using op codes
private
Assembly EmitAssembly(int theValue)
{
// create assembly name
AssemblyName assemblyName =
new AssemblyName();
assemblyName.Name = "FactorialAssembly";
// create assembly with one module
AssemblyBuilder newAssembly =
Thread.GetDomain().DefineDynamicAssembly(assemblyName,
AssemblyBuilderAccess.Run);
ModuleBuilder newModule =
newAssembly.DefineDynamicModule("MFactorial");
// define a public class named "CFactorial" in
the assembly
TypeBuilder myType =
newModule.DefineType("CFactorial",
TypeAttributes.Public);
// Mark the class as implementing IFactorial.
myType.AddInterfaceImplementation(typeof(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.
Type[] paramTypes =
new Type[0];
Type returnType =
typeof(Int32);
MethodBuilder simpleMethod =
myType.DefineMethod("myfactorial",
MethodAttributes.Public |MethodAttributes.Virtual,
returnType, paramTypes);
// obtain an ILGenerator to emit the IL
ILGenerator generator =
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 (Int32
i = 1; i <= theValue; ++i)
{
generator.Emit(OpCodes.Ldc_I4, i);
generator.Emit(OpCodes.Mul);
}
// 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
MethodInfo factorialInfo =typeof(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;
}
// check if the interface is null, generate
assembly
// otherwise it is already there, where it
is to be...
public
double DoFactorial(int theValue)
{
if (thesample ==
null)
{
GenerateCode(theValue);
}
// call the method through the interface
return (thesample.myfactorial());
}
// emit the assembly, create an instance and
// get the interface IFactorial
public void
GenerateCode(int theValue)
{
Assembly theAssembly =
EmitAssembly(theValue);
thesample = (IFactorial)theAssembly.CreateInstance("CFactorial");
}
// private member data
IFactorial thesample =
null;
}
class
Class1
{
[STAThread]
static void
Main(string[] args)
{
Int32 aValue = 5;
SampleFactorialFromEmission t =
new
SampleFactorialFromEmission();
double result = t.DoFactorial(aValue);
Console.WriteLine("Factorial
of " + aValue + " is " + result);
}
}
Conclusion
Hope this article would have helped you in understanding the
Reflection and Reflection.Emit in C#. See other articles on the website on .NET and C#.
|
The Complete Visual
C# Programmer's Guide covers most of the major components that make
up C# and the .net environment. The book is geared toward the
intermediate programmer, but contains enough material to satisfy the
advanced developer. |