C# Language
ILGenerador
Buscar..
Crea un DynamicAssembly que contiene un método auxiliar de UnixTimestamp
Este ejemplo muestra el uso de ILGenerator mediante la generación de código que hace uso de miembros ya existentes y nuevos creados, así como el manejo básico de excepciones. El siguiente código emite un DynamicAssembly que contiene un equivalente a este código c #:
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 methods
var 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 members
var 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 stack
cctorGen.Emit(OpCodes.Ldc_I4_1);
cctorGen.Emit(OpCodes.Ldc_I4_1);
cctorGen.Emit(OpCodes.Newobj, dateTimeCtor); //Call the constructor
cctorGen.Emit(OpCodes.Stsfld, epochTimeField); //Store the object in the static field
cctorGen.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 block
methodGen.Emit(OpCodes.Ldarga_S, (byte) 0); //To call a method on a struct we need to load the address of it
methodGen.Emit(OpCodes.Ldsfld, epochTimeField);
//Load the object of the static field we created as argument for the following call
methodGen.Emit(OpCodes.Call, dateTimeSubstract); //Call the substract method on the input DateTime
methodGen.Emit(OpCodes.Stloc_0); //Store the resulting TimeSpan in a local
methodGen.Emit(OpCodes.Ldloca_S, (byte) 0); //Load the locals address to call a method on it
methodGen.Emit(OpCodes.Call, timeSpanSecondsGetter); //Call the TotalSeconds Get method on the TimeSpan
methodGen.Emit(OpCodes.Conv_Ovf_I4); //Convert the result to Int32; throws an exception on overflow
methodGen.Emit(OpCodes.Stloc_1); //store the result for returning later
//The leave instruction to jump behind the catch block will be automatically emitted
methodGen.BeginCatchBlock(typeof (OverflowException)); //Begin the catch block
//When we are here, an OverflowException was thrown, that is now on the stack
methodGen.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 stack
methodGen.Emit(OpCodes.Ldloc_2); //Load the exception again
methodGen.Emit(OpCodes.Newobj, invalidOperationCtor);
//Create an InvalidOperationException with our message and inner Exception
methodGen.Emit(OpCodes.Throw); //Throw the created exception
methodGen.EndExceptionBlock(); //End the catch block
//When we are here, everything is fine
methodGen.Emit(OpCodes.Ldloc_1); //Load the result value
methodGen.Emit(OpCodes.Ret); //Return it
dynType.CreateType();
dynAsm.Save(an.Name + ".dll");
Crear anulación de método
Este ejemplo muestra cómo anular el método ToString
en la clase generada
// create an Assembly and new type
var 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 method
var toStr = typeBuilder.DefineMethod(
"ToString", // name
MethodAttributes.Public | MethodAttributes.Virtual, // modifiers
typeof(string), // return type
Type.EmptyTypes); // argument types
var ilGen = toStr.GetILGenerator();
ilGen.Emit(OpCodes.Ldstr, "Hello, world!");
ilGen.Emit(OpCodes.Ret);
// set this method as override of object.ToString
typeBuilder.DefineMethodOverride(toStr, typeof(object).GetMethod("ToString"));
var type = typeBuilder.CreateType();
// now test it:
var instance = Activator.CreateInstance(type);
Console.WriteLine(instance.ToString());
Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow