A Developers Introduction to Dynamic Assemblies
Intended Readers: Persons having
basic knowledge of assemblies, IL, meta data.
Platform: .NET 1.1
Level: Intermediate
Introduction
Assembly
is a basic deployment unit in .NET framework it contains types, resources code
etc. Assemblies can be classified into two types static and dynamic. Static
assemblies are the normal assemblies build by the .NET enabled compilers and
other tool such is al (Assembly Linker). Once build static assemblies
cannot be changed although we can dynamically loads them or create type from
them but still their nature is static.
Dynamic
assemblies are build at runtime and we can add the contents dynamically or
alter the existing one or can also emit the IL code directly in assembly,
normally this happens in the memory but in the end we can also persist the
dynamic assembly as well. .NET framework provides all these services in
System.Reflection.Emit namespace. System.Reflection.Emit contains various builder classes
which provide abstract access for the code emition. For example TypeBuilder
class is used to dynamically defining the type; AssemblyBuilder is used for
building and persisting the dynamic assembly etc.
Dynamic
Assemblies in Action
Lets look
an example in which following type will generate by the program through
System.Reflection.Emit classes
class
MyDynamicType
{
public MyDynamicType()
{
Console.WriteLine("Constructor
Called");
}
public void
MyMethod()
{
Console.WriteLine("Method
Called");
}
}
First step is to create
the dynamic instance of assembly, this can be done through
AppDomain.DefineDynamicAssembly () i.e.
AssemblyName
asmname = new AssemblyName();
asmname.Name =
"DynAsm.dll";
// Dynamic assembly
supports dynamic execution and persistence
AssemblyBuilder
asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(asmname,
AssemblyBuilderAccess.RunAndSave);
AssemblyName
class provides the access for various features of assembly such as KeyPair,
Culture. Second
step is to create the dynamic module which in turns responsible for creating
dynamic types.
// name of module
and assembly
ModuleBuilder
modBuilder = asmBuilder.DefineDynamicModule("MyModule",
"MyAsm.dll");
// defining the
public type
TypeBuilder
typeBuilder = modBuilder.DefineType("MyDynamicType",
TypeAttributes.Public);
After getting the
reference of typeBuilder we can add members in it i.e.
// zero arg constructor for
new type
ConstructorBuilder
defctor = typeBuilder.DefineConstructor(MethodAttributes.Public,
CallingConventions.Standard, new Type[0]);
Then comes the important
task generation of actual IL code, this can be done through ILGenerator
interface (which emits the actual IL Code), i.e.
ILGenerator
ctorIlGen = defctor.GetILGenerator();
//
Console.WriteLine(...)
ctorIlGen.EmitWriteLine("Constructor
Called");
// emit Return
statements
ctorIlGen.Emit(OpCodes.Ret);
OpCodes enum represents
the IL opcodes such as method call, load string, method return etc. Detail
knowledge of all IL opcodes can be found at MSDN.
The above process will
remains same for all methods, after emiting the IL its time to Emit the IL code
and dynamically calls it, typeBuilder.CreateType() is responsible for emiting
the underlying IL code and returns the Type object.
Type t =
typeBuilder.CreateType();
// create the
object instance from the Type
object obj =
Activator.CreateInstance(t);
// its time to save
all the work in Assembly
asmBuilder.Save("MyAsm.dll");
The complete code of
above process is
AssemblyName name = new AssemblyName();
name.Name = "MyAsm";
AssemblyBuilder asmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(name,
AssemblyBuilderAccess.RunAndSave);
// Get Module Builder
ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule("MyModule", "MyAsm.dll");
// Get Type Builder
TypeBuilder typeBuilder =
modBuilder.DefineType("MyDynamicType", TypeAttributes.Public);
// zero arg constructor
ConstructorBuilder defctor =
typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[0]);
ILGenerator ctorIlGen = defctor.GetILGenerator();
ctorIlGen.EmitWriteLine("Constructor Called");
ctorIlGen.Emit(OpCodes.Ret);
MethodBuilder mBuilder = typeBuilder.DefineMethod("MyMethod", MethodAttributes.Public, typeof(void), new Type[0]);
ILGenerator ilgen = mBuilder.GetILGenerator();
ilgen.EmitWriteLine("Method Called");
ilgen.Emit(OpCodes.Ret);
Type t =
typeBuilder.CreateType();
object obj = Activator.CreateInstance(t);
// invoke the obj.mymethod()
t.InvokeMember("MyMethod", BindingFlags.InvokeMethod, null, obj, null);
asmBuilder.Save("MyAsm.dll");
Once the assembly is generate you can use it in other
applications
Advantages
of Dynamic Assemblies
- Dynamic Assemblies provides great flexibility for
developers
- Primarily used by assembly generation tools, script
engines and compiler vendors
- We can create new .NET languages with different
specifications and syntax.
- Directly emiting IL means we are bypassing the
specifications and limitations of existing languages results efficient IL
code.
- Generate types and code as needed, which can results efficient
downloading of assemblies from network locations.
Your
Comments and Feedbacks are always welcome