公共语言运行时在 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!);