我正在制作一個在運行時生成包裝器的類,它得到一個這樣的類:
class Test
{
public int A { get; set; }
public int B { get; set; }
public string C { get; set; }
public DateTimeOffset D { get; set; }
public bool E { get; set; }
}
并創建一個包裝型別,如
class newTest
{
private Test _inner;
public newTest(Test inner)
{
_inner = inner;
}
public int A { get => _inner.A; }
public int B { get => _inner.B; }
public string C
{
get => _inner.C;
}
public DateTime D
{
get => _inner.D.UtcDateTime;
}
public short E
{
get => (short)(_inner.E ? 1 : 0);
}
}
并且一切正常,除了屬性 D 的 getter(只需要 UTC 時區中的 DateTime 部分)。為 DateTimeOffset 添加屬性的代碼是
private static void AddDateTimeOffsetProperty(TypeBuilder typeBuilder, FieldInfo fieldBuilder,
PropertyInfo field)
{
var propBuilder = typeBuilder.DefineProperty(field.Name
, PropertyAttributes.HasDefault
, typeof(DateTime)
, null);
var getBuilder = typeBuilder.DefineMethod($"get_{field.Name}",
_getAttr,
typeof(DateTime),
Type.EmptyTypes);
ILGenerator ilGen = getBuilder.GetILGenerator();
// load args to memory
ilGen.Emit(OpCodes.Ldarg_0);
// load required field from "_inner" obj
ilGen.Emit(OpCodes.Ldfld, fieldBuilder);
// call _inner getter
ilGen.EmitCall(OpCodes.Callvirt, field.GetMethod!, Array.Empty<Type>());
// alloc local variable
ilGen.Emit(OpCodes.Stloc_0);
ilGen.Emit(OpCodes.Ldloca_S);
MethodInfo utcDateTimeMethod = typeof(DateTimeOffset).GetProperty("UtcDateTime").GetMethod;
// get UtcDateTime
ilGen.EmitCall(OpCodes.Call, utcDateTimeMethod!, Array.Empty<Type>());
// return
ilGen.Emit(OpCodes.Ret);
propBuilder.SetGetMethod(getBuilder);
}
這里的“欄位”是內部類的屬性。生成類后,創建Test類的實體并包裝它
var testObj = new Test
{
A = 7,
B = 6,
C = "World",
E = false,
D = DateTimeOffset.UtcNow.ToOffset(TimeSpan.FromHours(7)),
F = 14
};
var wrapper = Activator.CreateInstance(wrapperTypeForTest, testObj);
之后wrapper.D將回傳“公共語言運行時檢測到無效程式。”,其他屬性作業。
uj5u.com熱心網友回復:
您在插槽 0(帶有Stloc_0)中存盤了一些東西,但您從未宣告過插槽 0。
您需要先宣告一個本地,這樣ILGenerator才能真正創建插槽來存盤您的變數。
LocalBuilder local = ilGen.DeclareLocal(typeof(DateTimeOffset));
接著:
ilGen.Emit(OpCodes.Stloc, local);
您正在使用Ldloca_S加載本地插槽的地址,但您從未通過該插槽進行實際加載。
ilGen.Emit(OpCodes.Ldloca, local);
(如果合適,ILGenerator將把它變成Ldloca_S)。
從檔案中ILGenerator.EmitCall:
將 call 或 callvirt 指令放入 Microsoft 中間語言 (MSIL) 流以呼叫可變引數方法。
您不是在呼叫 varargs 方法。Varargs 方法很少見。您正在呼叫普通方法,因此只需:
ilGen.Emit(OpCodes.Callvirt, field.GetMethod!);
和:
ilGen.Emit(OpCodes.Call, utcDateTimeMethod!);
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/394487.html
下一篇:使用R查找特定天/月前的日期
