C# | ILGenerator
Creates a DynamicAssembly that contains a UnixTimestamp helper method
Section titled “Creates a DynamicAssembly that contains a UnixTimestamp helper method”This example shows the usage of the ILGenerator by generating code that makes use of already existing and new created members as well as basic Exception handling. The following code emits a DynamicAssembly that contains an equivalent to this c# code:
public static class UnixTimeHelper{ private readonly static DateTime EpochTime = new DateTime(1970, 1, 1);
public static int UnixTimestamp(DateTime input) { int totalSeconds; try { totalSeconds = checked((int)input.Subtract(UnixTimeHelper.EpochTime).TotalSeconds); } catch (OverflowException overflowException) { throw new InvalidOperationException("It's to late for an Int32 timestamp.", overflowException); } return totalSeconds; }}//Get the required methodsvar dateTimeCtor = typeof (DateTime) .GetConstructor(new[] {typeof (int), typeof (int), typeof (int)});var dateTimeSubstract = typeof (DateTime) .GetMethod(nameof(DateTime.Subtract), new[] {typeof (DateTime)});var timeSpanSecondsGetter = typeof (TimeSpan) .GetProperty(nameof(TimeSpan.TotalSeconds)).GetGetMethod();var invalidOperationCtor = typeof (InvalidOperationException) .GetConstructor(new[] {typeof (string), typeof (Exception)});
if (dateTimeCtor == null || dateTimeSubstract == null || timeSpanSecondsGetter == null || invalidOperationCtor == null){ throw new Exception("Could not find a required Method, can not create Assembly.");}
//Setup the required membersvar an = new AssemblyName("UnixTimeAsm");var dynAsm = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.RunAndSave);var dynMod = dynAsm.DefineDynamicModule(an.Name, an.Name + ".dll");
var dynType = dynMod.DefineType("UnixTimeHelper", TypeAttributes.Abstract | TypeAttributes.Sealed | TypeAttributes.Public);
var epochTimeField = dynType.DefineField("EpochStartTime", typeof (DateTime), FieldAttributes.Private | FieldAttributes.Static | FieldAttributes.InitOnly);
var cctor = dynType.DefineConstructor( MethodAttributes.Private | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName | MethodAttributes.Static, CallingConventions.Standard, Type.EmptyTypes);
var cctorGen = cctor.GetILGenerator();cctorGen.Emit(OpCodes.Ldc_I4, 1970); //Load the DateTime constructor arguments onto the stackcctorGen.Emit(OpCodes.Ldc_I4_1);cctorGen.Emit(OpCodes.Ldc_I4_1);cctorGen.Emit(OpCodes.Newobj, dateTimeCtor); //Call the constructorcctorGen.Emit(OpCodes.Stsfld, epochTimeField); //Store the object in the static fieldcctorGen.Emit(OpCodes.Ret);
var unixTimestampMethod = dynType.DefineMethod("UnixTimestamp", MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.Static, CallingConventions.Standard, typeof (int), new[] {typeof (DateTime)});
unixTimestampMethod.DefineParameter(1, ParameterAttributes.None, "input");
var methodGen = unixTimestampMethod.GetILGenerator();methodGen.DeclareLocal(typeof (TimeSpan));methodGen.DeclareLocal(typeof (int));methodGen.DeclareLocal(typeof (OverflowException));
methodGen.BeginExceptionBlock(); //Begin the try blockmethodGen.Emit(OpCodes.Ldarga_S, (byte) 0); //To call a method on a struct we need to load the address of itmethodGen.Emit(OpCodes.Ldsfld, epochTimeField); //Load the object of the static field we created as argument for the following callmethodGen.Emit(OpCodes.Call, dateTimeSubstract); //Call the substract method on the input DateTimemethodGen.Emit(OpCodes.Stloc_0); //Store the resulting TimeSpan in a localmethodGen.Emit(OpCodes.Ldloca_S, (byte) 0); //Load the locals address to call a method on itmethodGen.Emit(OpCodes.Call, timeSpanSecondsGetter); //Call the TotalSeconds Get method on the TimeSpanmethodGen.Emit(OpCodes.Conv_Ovf_I4); //Convert the result to Int32; throws an exception on overflowmethodGen.Emit(OpCodes.Stloc_1); //store the result for returning later//The leave instruction to jump behind the catch block will be automatically emittedmethodGen.BeginCatchBlock(typeof (OverflowException)); //Begin the catch block//When we are here, an OverflowException was thrown, that is now on the stackmethodGen.Emit(OpCodes.Stloc_2); //Store the exception in a local.methodGen.Emit(OpCodes.Ldstr, "It's to late for an Int32 timestamp."); //Load our error message onto the stackmethodGen.Emit(OpCodes.Ldloc_2); //Load the exception againmethodGen.Emit(OpCodes.Newobj, invalidOperationCtor); //Create an InvalidOperationException with our message and inner ExceptionmethodGen.Emit(OpCodes.Throw); //Throw the created exceptionmethodGen.EndExceptionBlock(); //End the catch block//When we are here, everything is finemethodGen.Emit(OpCodes.Ldloc_1); //Load the result valuemethodGen.Emit(OpCodes.Ret); //Return it
dynType.CreateType();
dynAsm.Save(an.Name + ".dll");Create method override
Section titled “Create method override”This example shows how to override ToString method in generated class
// create an Assembly and new typevar name = new AssemblyName("MethodOverriding");var dynAsm = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.RunAndSave);var dynModule = dynAsm.DefineDynamicModule(name.Name, $"{name.Name}.dll");var typeBuilder = dynModule.DefineType("MyClass", TypeAttributes.Public | TypeAttributes.Class);
// define a new methodvar toStr = typeBuilder.DefineMethod( "ToString", // name MethodAttributes.Public | MethodAttributes.Virtual, // modifiers typeof(string), // return type Type.EmptyTypes); // argument typesvar ilGen = toStr.GetILGenerator();ilGen.Emit(OpCodes.Ldstr, "Hello, world!");ilGen.Emit(OpCodes.Ret);
// set this method as override of object.ToStringtypeBuilder.DefineMethodOverride(toStr, typeof(object).GetMethod("ToString"));var type = typeBuilder.CreateType();
// now test it:var instance = Activator.CreateInstance(type);Console.WriteLine(instance.ToString());