Chapter157:ILGenerator

No Comments

Section 157.1: Creates a DynamicAssembly that contains a UnixTimestamphelpermethod

ThisexampleshowstheusageoftheILGeneratorbygeneratingcodethatmakesuseofalreadyexistingandnew createdmembersaswellasbasicExceptionhandling.ThefollowingcodeemitsaDynamicAssemblythatcontains an equivalent to this c# code:

publicstaticclassUnixTimeHelper

{

privatereadonlystaticDateTimeEpochTime=newDateTime(1970,1,1);

publicstaticintUnixTimestamp(DateTimeinput)

{

inttotalSeconds; try

{

totalSeconds=checked((int)input.Subtract(UnixTimeHelper.EpochTime).TotalSeconds);

}

catch(OverflowExceptionoverflowException)

{

thrownewInvalidOperationException(“It’stolateforanInt32timestamp.”,overflowException);

}

returntotalSeconds;

}

}

//Gettherequiredmethods

vardateTimeCtor=typeof(DateTime)

.GetConstructor(new[]{typeof(int),typeof(int),typeof(int)});

vardateTimeSubstract=typeof(DateTime)

.GetMethod(nameof(DateTime.Subtract),new[]{typeof(DateTime)}); vartimeSpanSecondsGetter=typeof(TimeSpan)

.GetProperty(nameof(TimeSpan.TotalSeconds)).GetGetMethod(); varinvalidOperationCtor=typeof(InvalidOperationException)

.GetConstructor(new[]{typeof(string),typeof(Exception)});

if(dateTimeCtor==null||dateTimeSubstract==null|| timeSpanSecondsGetter==null||invalidOperationCtor==null)

{

thrownewException(“CouldnotfindarequiredMethod,cannotcreateAssembly.”);

}

//Setuptherequiredmembers

varan=newAssemblyName(“UnixTimeAsm”);

vardynAsm=AppDomain.CurrentDomain.DefineDynamicAssembly(an,AssemblyBuilderAccess.RunAndSave); vardynMod=dynAsm.DefineDynamicModule(an.Name,an.Name+”.dll”);

vardynType=dynMod.DefineType(“UnixTimeHelper”,

TypeAttributes.Abstract|TypeAttributes.Sealed|TypeAttributes.Public);

varepochTimeField=dynType.DefineField(“EpochStartTime”,typeof(DateTime), FieldAttributes.Private|FieldAttributes.Static|FieldAttributes.InitOnly);

varcctor=

dynType.DefineConstructor(

MethodAttributes.Private|MethodAttributes.HideBySig|MethodAttributes.SpecialName| MethodAttributes.RTSpecialName|MethodAttributes.Static, CallingConventions.Standard, Type.EmptyTypes);

varcctorGen=cctor.GetILGenerator();

cctorGen.Emit(OpCodes.Ldc_I4,1970);//LoadtheDateTimeconstructorargumentsontothestack

cctorGen.Emit(OpCodes.Ldc_I4_1);

cctorGen.Emit(OpCodes.Ldc_I4_1);

cctorGen.Emit(OpCodes.Newobj,dateTimeCtor);//Calltheconstructor cctorGen.Emit(OpCodes.Stsfld,epochTimeField);//StoretheobjectinthestaticfieldcctorGen.Emit(OpCodes.Ret);

varunixTimestampMethod=dynType.DefineMethod(“UnixTimestamp”, MethodAttributes.Public|MethodAttributes.HideBySig|MethodAttributes.Static, CallingConventions.Standard,typeof(int),new[]{typeof(DateTime)});

unixTimestampMethod.DefineParameter(1,ParameterAttributes.None,”input”);

varmethodGen=unixTimestampMethod.GetILGenerator();

methodGen.DeclareLocal(typeof(TimeSpan));

methodGen.DeclareLocal(typeof(int));

methodGen.DeclareLocal(typeof(OverflowException));

methodGen.BeginExceptionBlock();//Beginthetryblock

methodGen.Emit(OpCodes.Ldarga_S,(byte)0);//Tocallamethodonastructweneedtoloadthe addressof it

methodGen.Emit(OpCodes.Ldsfld,epochTimeField);

//Loadtheobjectofthestaticfieldwecreatedasargumentforthefollowingcall methodGen.Emit(OpCodes.Call,dateTimeSubstract);//CallthesubstractmethodontheinputDateTime methodGen.Emit(OpCodes.Stloc_0);//StoretheresultingTimeSpaninalocal methodGen.Emit(OpCodes.Ldloca_S,(byte)0);//Loadthelocalsaddresstocallamethodon it methodGen.Emit(OpCodes.Call,timeSpanSecondsGetter);//CalltheTotalSecondsGetmethodontheTimeSpan

methodGen.Emit(OpCodes.Conv_Ovf_I4);//ConverttheresulttoInt32;throwsanexceptiononoverflow

methodGen.Emit(OpCodes.Stloc_1);//storetheresultforreturninglater

//Theleaveinstructiontojumpbehindthecatchblockwillbeautomaticallyemitted

methodGen.BeginCatchBlock(typeof(OverflowException));//Beginthecatchblock

//Whenwearehere,anOverflowExceptionwasthrown,thatisnowonthestack methodGen.Emit(OpCodes.Stloc_2);//Storetheexceptioninalocal. methodGen.Emit(OpCodes.Ldstr,”It’stolateforanInt32timestamp.”);

//Loadourerrormessageontothestack

methodGen.Emit(OpCodes.Ldloc_2);//Loadtheexceptionagain methodGen.Emit(OpCodes.Newobj,invalidOperationCtor);

//CreateanInvalidOperationExceptionwithourmessageandinnerException methodGen.Emit(OpCodes.Throw);//Throwthecreatedexception methodGen.EndExceptionBlock();//Endthecatchblock

//Whenwearehere,everythingisfine

methodGen.Emit(OpCodes.Ldloc_1);//Loadtheresultvalue

methodGen.Emit(OpCodes.Ret);//Return it

dynType.CreateType();

dynAsm.Save(an.Name+”.dll”);

Section157.2:Createmethodoverride

ThisexampleshowshowtooverrideToStringmethodingeneratedclass

//createanAssemblyandnewtype

varname=newAssemblyName(“MethodOverriding”);

vardynAsm=AppDomain.CurrentDomain.DefineDynamicAssembly(name,AssemblyBuilderAccess.RunAndSave);

vardynModule=dynAsm.DefineDynamicModule(name.Name,$”{name.Name}.dll”);

vartypeBuilder=dynModule.DefineType(“MyClass”,TypeAttributes.Public|TypeAttributes.Class);

//defineanewmethod

vartoStr=typeBuilder.DefineMethod(

“ToString”,//name

MethodAttributes.Public|MethodAttributes.Virtual,//modifiers

typeof(string),//returntype

Type.EmptyTypes);//argumenttypes

varilGen=toStr.GetILGenerator();

ilGen.Emit(OpCodes.Ldstr,”Hello,world!”);

ilGen.Emit(OpCodes.Ret);

//setthismethodasoverrideofobject.ToString typeBuilder.DefineMethodOverride(toStr,typeof(object).GetMethod(“ToString”)); vartype=typeBuilder.CreateType();

//nowtestit:

varinstance=Activator.CreateInstance(type);

Console.WriteLine(instance.ToString());

About us and this blog

We are a digital marketing company with a focus on helping our customers achieve great results across several key areas.

Request a free quote

We offer professional SEO services that help websites increase their organic search score drastically in order to compete for the highest rankings even when it comes to highly competitive keywords.

Subscribe to our newsletter!

More from our blog

See all posts

Leave a Comment