.NET Framework

System.Reflection.Emit namespace

Creating an assembly dynamically

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
 }
}

Source
.NET Framework Notes for Professionals book

Ahmed Mohamed Abd ElMajeed

Web developer and Server Administrator, Founder of Akwad web School and Programmer Notes.

Related Articles

Leave a Reply

Your email address will not be published. Required fields are marked *

Check Also

Close
Close

Adblock Detected

Please consider supporting us by disabling your ad blocker