using System;
using System.Reflection;
using System.Reflection.Emit;
class DemoAssemblyBuilder
{
public static void Main()
{
// An assembly consists of one or more modules, each of which
// contains zero or more types. This code creates a single-module
// assembly, the most common case. The module contains one type,
// named "MyDynamicType", that has a private field, a property
// that gets and sets the private field, constructors that
// initialize the private field, and a method that multiplies
// a user-supplied number by the private field value and returns
// the result. In C# the type might look like this:
/*
public class MyDynamicType
{
private int m_number;
public MyDynamicType() : this(42) {}
public MyDynamicType(int initNumber)
{
m_number = initNumber;
}
public int Number
{
get { return m_number; }
set { m_number = value; }
}
public int MyMethod(int multiplier)
{
return m_number * multiplier;
}
}
*/
AssemblyName aName = new AssemblyName("DynamicAssemblyExample");
AssemblyBuilder ab =
AppDomain.CurrentDomain.DefineDynamicAssembly(
aName,
AssemblyBuilderAccess.RunAndSave);
// For a single-module assembly, the module name is usually
// the assembly name plus an extension.
ModuleBuilder mb =
ab.DefineDynamicModule(aName.Name, aName.Name + ".dll");
TypeBuilder tb = mb.DefineType(
"MyDynamicType",
TypeAttributes.Public);
// Add a private field of type int (Int32).
FieldBuilder fbNumber = tb.DefineField(
"m_number",
typeof(int),
FieldAttributes.Private);
// Next, we make a simple sealed method.
MethodBuilder mbMyMethod = tb.DefineMethod(
"MyMethod",
MethodAttributes.Public,
typeof(int),
new[] { typeof(int) });
ILGenerator il = mbMyMethod.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // Load this - always the first argument of any instance method
il.Emit(OpCodes.Ldfld, fbNumber);
il.Emit(OpCodes.Ldarg_1); // Load the integer argument
il.Emit(OpCodes.Mul); // Multiply the two numbers with no overflow checking
il.Emit(OpCodes.Ret); // Return
// Next, we build the property. This involves building the property itself, as well as the
// getter and setter methods.
PropertyBuilder pbNumber = tb.DefineProperty(
"Number", // Name
PropertyAttributes.None,
typeof(int), // Type of the property
new Type[0]); // Types of indices, if any
MethodBuilder mbSetNumber = tb.DefineMethod(
"set_Number", // Name - setters are set_Property by convention
// Setter is a special method and we don't want it to appear to callers from C#
MethodAttributes.PrivateScope | MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.SpecialName,
typeof(void), // Setters don't return a value
new[] { typeof(int) }); // We have a single argument of type System.Int32
// To generate the body of the method, we'll need an IL generator
il = mbSetNumber.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // Load this
il.Emit(OpCodes.Ldarg_1); // Load the new value
il.Emit(OpCodes.Stfld, fbNumber); // Save the new value to this.m_number
il.Emit(OpCodes.Ret); // Return
// Finally, link the method to the setter of our property
pbNumber.SetSetMethod(mbSetNumber);
MethodBuilder mbGetNumber = tb.DefineMethod(
"get_Number",
MethodAttributes.PrivateScope | MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.SpecialName,
typeof(int),
new Type[0]);
il = mbGetNumber.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // Load this
il.Emit(OpCodes.Ldfld, fbNumber); // Load the value of this.m_number
il.Emit(OpCodes.Ret); // Return the value
pbNumber.SetGetMethod(mbGetNumber);
// Finally, we add the two constructors.
// Constructor needs to call the constructor of the parent class, or another constructor in the same class
ConstructorBuilder intConstructor = tb.DefineConstructor(
MethodAttributes.Public, CallingConventions.Standard | CallingConventions.HasThis, new[] { typeof(int) });
il = intConstructor.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // this
il.Emit(OpCodes.Call, typeof(object).GetConstructor(new Type[0])); // call parent's constructor
il.Emit(OpCodes.Ldarg_0); // this
il.Emit(OpCodes.Ldarg_1); // our int argument
il.Emit(OpCodes.Stfld, fbNumber); // store argument in this.m_number
il.Emit(OpCodes.Ret);
var parameterlessConstructor = tb.DefineConstructor(
MethodAttributes.Public, CallingConventions.Standard | CallingConventions.HasThis, new Type[0]);
il = parameterlessConstructor.GetILGenerator();
il.Emit(OpCodes.Ldarg_0); // this
il.Emit(OpCodes.Ldc_I4_S, (byte)42); // load 42 as an integer constant
il.Emit(OpCodes.Call, intConstructor); // call this(42)
il.Emit(OpCodes.Ret);
// And make sure the type is created
Type ourType = tb.CreateType();
// The types from the assembly can be used directly using reflection, or we can save the assembly to use as a reference
object ourInstance = Activator.CreateInstance(ourType);
Console.WriteLine(ourType.GetProperty("Number").GetValue(ourInstance)); // 42
// Save the assembly for use elsewhere. This is very useful for debugging - you can use e.g. ILSpy to look at the equivalent IL/C# code.
ab.Save(@"DynamicAssemblyExample.dll");
// Using newly created type
var myDynamicType = tb.CreateType();
var myDynamicTypeInstance = Activator.CreateInstance(myDynamicType);
Console.WriteLine(myDynamicTypeInstance.GetType()); // MyDynamicType
var numberField = myDynamicType.GetField("m_number", BindingFlags.NonPublic | BindingFlags.Instance);
numberField.SetValue (myDynamicTypeInstance, 10);
Console.WriteLine(numberField.GetValue(myDynamicTypeInstance)); // 10
}
}