gpt4 book ai didi

c# - 为方法发出代码时取消装箱可为 Nullable 使评估堆栈处于意外(对我而言)状态

转载 作者:太空狗 更新时间:2023-10-30 00:41:16 25 4
gpt4 key购买 nike

概述(请原谅我说得这么详细,但我宁愿它太多也不愿太少):我正在尝试为我们的解决方案编辑 Dapper 源代码,以便在读取任何 DateTime 或 Nullable 时从数据库中,它的 DateTime.Kind 属性始终设置为 DateTimeKind.Utc。

在我们的系统中,保证来自前端的所有 DateTimes 都是 UTC 时间,并且数据库 (Sql Server Azure) 将它们存储为 UTC 中的 DateTime 类型(我们没有使用 DateTimeOffsets,我们只是一直在制作在将日期时间存储在数据库中之前确保日期时间是 UTC。)

我一直在阅读有关如何使用 ILGenerator.Emit(...) 为 DynamicMethods 生成代码的所有内容,并且感觉我对它如何与评估堆栈、局部变量等一起工作有很好的理解。在我的努力中为了解决这个问题,我编写了一些小代码示例来帮助我实现最终目标。我写了一个 DynamicMethod 以 DateTime 作为参数,调用 DateTime.SpecifyKind,返回值。那么 DateTime 也一样吗?类型,使用其 Nullable.Value 属性获取 SpecifyKind 方法的 DateTime。

这就是我的问题所在:在 dapper 中,DateTime(或 DateTime?我实际上并不知道,但当我把它当作它时,我没有得到我期望的)是装箱的。因此,当我尝试使用 OpCodes.Unbox 或 OpCodes.Unbox_Any,然后将结果视为 DateTime 或 DateTime? 时,我得到一个 VerificationException:操作可能使运行时不稳定。

显然,我遗漏了一些关于装箱的重要信息,但我会给你我的代码示例,也许你可以帮助我让它工作。

这个有效:

    [Test]
public void Reflection_Emit_Test3()
{
//Setup
var dm = new DynamicMethod("SetUtc", typeof(DateTime?), new Type[] {typeof(DateTime?)});

var nullableType = typeof(DateTime?);

var il = dm.GetILGenerator();

il.Emit(OpCodes.Ldarga_S, 0); // [DateTime?]
il.Emit(OpCodes.Call, nullableType.GetProperty("Value").GetGetMethod()); // [DateTime]
il.Emit(OpCodes.Ldc_I4, (int)DateTimeKind.Utc); // [DateTime][Utc]
il.Emit(OpCodes.Call, typeof(DateTime).GetMethod("SpecifyKind")); //[DateTime]
il.Emit(OpCodes.Newobj, nullableType.GetConstructor(new[] {typeof (DateTime)})); //[DateTime?]
il.Emit(OpCodes.Ret);

var meth = (Func<DateTime?, DateTime?>)dm.CreateDelegate(typeof(Func<DateTime?, DateTime?>));

DateTime? now = DateTime.Now;

Assert.That(now.Value.Kind, Is.Not.EqualTo(DateTimeKind.Utc));

//Act

var nowUtc = meth(now);

//Verify

Assert.That(nowUtc.Value.Kind, Is.EqualTo(DateTimeKind.Utc));
}

我在这里得到了我所期望的。好极了!但这还没有结束,因为我们还有开箱要处理……

    [Test]
public void Reflection_Emit_Test4()
{
//Setup
var dm = new DynamicMethod("SetUtc", typeof(DateTime?), new Type[] { typeof(object) });

var nullableType = typeof(DateTime?);

var il = dm.GetILGenerator();
il.DeclareLocal(typeof (DateTime?));

il.Emit(OpCodes.Ldarga_S, 0); // [object]
il.Emit(OpCodes.Unbox_Any, typeof(DateTime?)); // [DateTime?]
il.Emit(OpCodes.Call, nullableType.GetProperty("Value").GetGetMethod()); // [DateTime]
il.Emit(OpCodes.Ldc_I4, (int)DateTimeKind.Utc); // [DateTime][Utc]
il.Emit(OpCodes.Call, typeof(DateTime).GetMethod("SpecifyKind")); //[DateTime]
il.Emit(OpCodes.Newobj, nullableType.GetConstructor(new[] { typeof(DateTime) })); //[DateTime?]
il.Emit(OpCodes.Ret);

var meth = (Func<object, DateTime?>)dm.CreateDelegate(typeof(Func<object, DateTime?>));

object now = new DateTime?(DateTime.Now);

Assert.That(((DateTime?) now).Value.Kind, Is.Not.EqualTo(DateTimeKind.Utc));

//Act

var nowUtc = meth(now);

//Verify

Assert.That(nowUtc.Value.Kind, Is.EqualTo(DateTimeKind.Utc));
}

这只是直线上升不会运行。我得到了 VerificationException,然后我在角落里哭了一会儿,直到我准备好再试一次。

我试过期待 DateTime 而不是 DateTime? (拆箱后,假设评估堆栈上的 DateTime,而不是 DateTime?)但这也失败了。

有人可以告诉我我缺少什么吗?

最佳答案

如有疑问,请编写一个执行相同操作的最小 C# 库,然后查看编译后的结果:

你的尝试似乎等同于

using System;

static class Program {
public static DateTime? SetUtc(object value) {
return new DateTime?(DateTime.SpecifyKind(((DateTime?)value).Value, DateTimeKind.Utc));
}
};

这编译为:

$ mcs test.cs -target:library -optimize+ && monodis test.dll...        IL_0000:  ldarg.0         IL_0001:  unbox.any valuetype [mscorlib]System.Nullable`1<valuetype [mscorlib]System.DateTime>        IL_0006:  stloc.0         IL_0007:  ldloca.s 0        IL_0009:  call instance !0 valuetype [mscorlib]System.Nullable`1<valuetype [mscorlib]System.DateTime>::get_Value()        IL_000e:  ldc.i4.1         IL_000f:  call valuetype [mscorlib]System.DateTime valuetype [mscorlib]System.DateTime::SpecifyKind(valuetype [mscorlib]System.DateTime, valuetype [mscorlib]System.DateTimeKind)        IL_0014:  newobj instance void valuetype [mscorlib]System.Nullable`1<valuetype [mscorlib]System.DateTime>::'.ctor'(!0)        IL_0019:  ret ...

Thee first difference with your version is that ldarg is used instead of ldarga. You want unbox.any to check the passed value, not a pointer to the passed value. (ldarga does work too in my tests, but ldarg makes more sense anyway.)

The second, and more relevant, difference with your version is that after unbox.any, the value is stored, and then a reference to that location is loaded. This is because the implicit this parameter of instance methods of value types have type ref T, rather than the T you're used to for instance methods of reference types. If I do include that stloc.0/ldloca.s 0, your code then passes its test, on my system.

However, as you unconditionally read the Value property after casting to DateTime?, you might as well cast straight to DateTime and avoid the problem entirely. The only difference would be which exception you get when a value of the wrong type is passed in.

If you instead want something like

public static DateTime? SetUtc(object value) {
var local = value as DateTime?;
return local == null ? default(DateTime?) : DateTime.SpecifyKind(local.Value, DateTimeKind.Utc);
}

然后我会用类似的东西

var label1 = il.DefineLabel();
var label2 = il.DefineLabel();

il.Emit(OpCodes.Ldarg_S, 0); // object
il.Emit(OpCodes.Isinst, typeof(DateTime)); // boxed DateTime
il.Emit(OpCodes.Dup); // boxed DateTime, boxed DateTime
il.Emit(OpCodes.Brfalse_S, label1); // boxed DateTime
il.Emit(OpCodes.Unbox_Any, typeof(DateTime)); // unboxed DateTime
il.Emit(OpCodes.Ldc_I4_1); // unboxed DateTime, int
il.Emit(OpCodes.Call, typeof(DateTime).GetMethod("SpecifyKind")); // unboxed DateTime
il.Emit(OpCodes.Newobj, typeof(DateTime?).GetConstructor(new[] { typeof(DateTime) })); // unboxed DateTime?
il.Emit(OpCodes.Br_S, label2);

il.MarkLabel(label1); // boxed DateTime (known to be null)
il.Emit(OpCodes.Unbox_Any, typeof(DateTime?)); // unboxed DateTime?

il.MarkLabel(label2); // unboxed DateTime?
il.Emit(OpCodes.Ret);

关于c# - 为方法发出代码时取消装箱可为 Nullable 使评估堆栈处于意外(对我而言)状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21711255/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com