公共语言运行时在 DateTime getter 中检测到无效程序
Common Language Runtime detected an invalid program in DateTime getter
我正在制作一个 class 用于在运行时生成包装器,它得到一个 class 像这样:
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);
}
}
并且一切正常,除了 getter 用于 属性 D(只需要 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);
}
这里的“字段”是内部class的属性。
class 生成后,创建 Test class 的实例并包装它
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
将 return“公共语言运行时检测到无效程序。”,其他属性起作用。
您在插槽 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
)。
来自docs for ILGenerator.EmitCall
:
Puts a call or callvirt instruction onto the Microsoft intermediate language (MSIL) stream to call a varargs method.
您没有调用可变参数方法。 Varargs 方法少之又少。您正在调用一个普通方法,所以只需:
ilGen.Emit(OpCodes.Callvirt, field.GetMethod!);
和:
ilGen.Emit(OpCodes.Call, utcDateTimeMethod!);
我正在制作一个 class 用于在运行时生成包装器,它得到一个 class 像这样:
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);
}
}
并且一切正常,除了 getter 用于 属性 D(只需要 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);
}
这里的“字段”是内部class的属性。 class 生成后,创建 Test class 的实例并包装它
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
将 return“公共语言运行时检测到无效程序。”,其他属性起作用。
您在插槽 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
)。
来自docs for ILGenerator.EmitCall
:
Puts a call or callvirt instruction onto the Microsoft intermediate language (MSIL) stream to call a varargs method.
您没有调用可变参数方法。 Varargs 方法少之又少。您正在调用一个普通方法,所以只需:
ilGen.Emit(OpCodes.Callvirt, field.GetMethod!);
和:
ilGen.Emit(OpCodes.Call, utcDateTimeMethod!);