鉴于以下情况,为什么会抛出 InvalidCastException?我不明白为什么它应该在错误之外(这是在 x86 中;x64 因 clrjit.dll 中的 0xC0000005 崩溃)。
class Program
{
static void Main(string[] args)
{
MyDouble? my = new MyDouble(1.0);
Boolean compare = my == 0.0;
}
struct MyDouble
{
Double? _value;
public MyDouble(Double value)
{
_value = value;
}
public static implicit operator Double(MyDouble value)
{
if (value._value.HasValue)
{
return value._value.Value;
}
throw new InvalidCastException("MyDouble value cannot convert to System.Double: no value present.");
}
}
}
这是生成的 CILMain()
:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 3
.locals init (
[0] valuetype [mscorlib]System.Nullable`1<valuetype Program/MyDouble> my,
[1] bool compare,
[2] valuetype [mscorlib]System.Nullable`1<valuetype Program/MyDouble> CS$0$0000,
[3] valuetype [mscorlib]System.Nullable`1<float64> CS$0$0001)
L_0000: nop
L_0001: ldloca.s my
L_0003: ldc.r8 1
L_000c: newobj instance void Program/MyDouble::.ctor(float64)
L_0011: call instance void [mscorlib]System.Nullable`1<valuetype Program/MyDouble>::.ctor(!0)
L_0016: nop
L_0017: ldloc.0
L_0018: stloc.2
L_0019: ldloca.s CS$0$0000
L_001b: call instance bool [mscorlib]System.Nullable`1<valuetype Program/MyDouble>::get_HasValue()
L_0020: brtrue.s L_002d
L_0022: ldloca.s CS$0$0001
L_0024: initobj [mscorlib]System.Nullable`1<float64>
L_002a: ldloc.3
L_002b: br.s L_003e
L_002d: ldloca.s CS$0$0000
L_002f: call instance !0 [mscorlib]System.Nullable`1<valuetype Program/MyDouble>::GetValueOrDefault()
L_0034: call float64 Program/MyDouble::op_Implicit(valuetype Program/MyDouble)
L_0039: newobj instance void [mscorlib]System.Nullable`1<float64>::.ctor(!0)
L_003e: stloc.3
L_003f: ldloca.s CS$0$0001
L_0041: call instance !0 [mscorlib]System.Nullable`1<float64>::GetValueOrDefault()
L_0046: call float64 Program/MyDouble::op_Implicit(valuetype Program/MyDouble)
L_004b: conv.r8
L_004c: ldc.r8 0
L_0055: bne.un.s L_0060
L_0057: ldloca.s CS$0$0001
L_0059: call instance bool [mscorlib]System.Nullable`1<float64>::get_HasValue()
L_005e: br.s L_0061
L_0060: ldc.i4.0
L_0061: stloc.1
L_0062: ret
}
请注意 IL 中的 0x2D - 0x3E 行。它检索MyDouble?
实例,调用GetValueOrDefault
对其调用隐式运算符,然后将结果包装在Double?
并将其存储在编译器生成的CS$0$0001
当地的。在 0x3F 到 0x55 行中,我们检索CS$0$0001
值,通过“展开”GetValueOrDefault
然后与0比较...但等一下!那个额外的电话是什么MyDouble::op_Implicit
在 0x46 线上做什么?
如果我们调试 C# 程序,我们确实会看到 2 个调用implicit operator Double(MyDouble value)
,这是第二次失败,因为value
未初始化。
这里发生了什么?
这显然是一个 C# 编译器错误。感谢您引起我的注意。
顺便说一句,使用用户定义的隐式转换运算符来引发异常是一种不好的做法;该文档指出隐式转换应该是那些永远不会抛出的转换。您确定不希望这是显式转换吗?
无论如何,回到错误。
该错误在 C# 3 和 4 中重现,但在 C# 2 中没有重现。这意味着这是我的错。当我重新编写用户定义的提升隐式运算符代码以使其与表达式树 lambda 一起使用时,我可能导致了该错误。对于那个很抱歉!该代码非常棘手,显然我没有对其进行充分的测试。
代码应该做的事情是:
首先,重载解析尝试解析 == 的含义。两个参数均有效的最佳 == 运算符是比较两个可为空双精度值的提升运算符。因此应分析为:
Boolean compare = (double?)my == (double?)0.0;
(如果您像这样编写代码,那么它在 C# 3 和 4 中会做正确的事情。)
提升后的 == 运算符的含义是:
- 评估两个参数
- 如果两者都为空,则结果为真——显然在这种情况下不会发生这种情况
- 如果一个为空而另一个不为则结果为 false
- 如果两者都不为空,则两者都被展开为双精度值并作为双精度值进行比较。
现在的问题是“评估左侧的正确方法是什么?”
我们这里有一个来自 MyDouble 的提升的用户定义转换运算符?加倍?正确的行为是:
- 如果“my”为空,则结果为空双精度?。
- 如果“my”不为 null,则结果是用户定义的 my.Value 到 double 的转换,然后将该 double 转换为 double?。
显然,这个过程中出现了问题。
我将在我们的数据库中输入一个错误,但任何修复都可能会错过将其纳入下一个服务包的更改的最后期限。如果我是你,我会寻找解决方法。再次对错误表示歉意。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)