C# Language
ILGenerator
Suche…
Erstellt eine DynamicAssembly, die eine UnixTimestamp-Hilfemethode enthält
In diesem Beispiel wird die Verwendung des ILGenerator veranschaulicht, indem Code generiert wird, der bereits vorhandene und neu erstellte Member sowie die grundlegende Behandlung von Ausnahmen verwendet. Der folgende Code gibt eine DynamicAssembly aus, die ein Äquivalent zu diesem C # -Code enthält:
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");
Methodenüberschreibung erstellen
Dieses Beispiel zeigt, wie die ToString
Methode in der generierten Klasse überschrieben wird
// 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
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow