The previous post in this miniseries got us as far as defining a type using the TypeBuilder object:
TypeBuilder simpleType = moduleBuilder.DefineType("PluginSimpleType", TypeAttributes.Class | TypeAttributes.Public);
TypeBuilder extendedType = moduleBuilder.DefineType("PluginExtendedType", TypeAttributes.Class | TypeAttributes.Public, typeof(Customer), new Type[] {typeof(IEqualityComparer), typeof(IEquatable<int>) });
The TypeBuilder object is the entry point to creating the members of the Type such as constructors and methods. Here’s how you can create a public constructor with a string parameter:
ConstructorBuilder constructorBuilder = simpleType.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new Type[] { typeof(string) });
If there’s no constructor for the Type then a default parameterless constructor is automatically created for you. If you declare an overloaded constructor like above then there will be no default constructor – this should be familiar to you from everyday OOP in .NET and Java. If you’d like to have a default empty constructor like this…
public Customer()
{
}
…besides the overloaded constructor then you can call the DefineDefaultConstructor method:
ConstructorBuilder defaultConstructorBuilder = simpleType.DefineDefaultConstructor(MethodAttributes.Public);
Otherwise if you’d like to have a parameterless constructor which has a body, i.e. does more than call the default constructor of the base class then you need to use the DefineConstructor method again:
ConstructorBuilder constructorBuilder = simpleType.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, null);
Note the ‘null’ to denote 0 parameters.
This is not the end of the story. We need to attach a method body to the constructors(s) defined by the DefineConstructor method. We can leave the parameterless default constructor as it is, we don’t need any method body implementation for it.
This is where creating assemblies in code can become quite challenging.
Creating your method body requires intimate knowledge of the Microsoft Intermediate Language instructions that all .NET languages are translated into when compiled. I won’t even pretend that I know much about these instructions. The ILGenerator object can somewhat help you out so that you don’t need to write the actual byte code.
The OpCodes class stores more than 1000 instructions such as Ret for return, Call to call another method or Neg for negating a value.
You can get the ILGenerator object from the builder and call its Emit method to emit IL instructions. This is how you’d add a return statement:
ILGenerator msilGenerator = constructorBuilder.GetILGenerator();
msilGenerator.Emit(OpCodes.Ret);
More information about dynamic methods is available on MSDN here and here.
View all posts on Reflection here.
Like this:
Like Loading...