class A
{
public static explicit operator A(long mm)
{
return null;
}
}
UInt64 ul = UInt64.MaxValue;
IntPtr ptr = (IntPtr)ul;//no error
A a = (A)ul;//Cannot convert type 'ulong' to 'A'
为什么IntPtr允许这种行为?
以下是IL代码:
.entrypoint
.maxstack 1
.locals init (
[0] uint64 ul,
[1] native int ptr)
L_0000: nop
L_0001: ldc.i4.m1
L_0002: conv.i8
L_0003: stloc.0
L_0004: ldloc.0
L_0005: call native int [mscorlib]System.IntPtr::op_Explicit(int64)
L_000a: stloc.1
L_000b: ret
答案 0 :(得分:3)
我同意这看起来有点奇怪,所以我进行了几次测试。
ulong ul = UInt64.MaxValue;
long l = Int64.MaxValue;
IntPtr ulptr = (IntPtr)ul;
IntPtr lptr = (IntPtr)l;
因为IntPtr
强制转换表明它可能会抛出OverflowException
,所以我期待(IntPtr)ul
强制转换会抛出异常。它没。想象一下,当(IntPtr)l
演员投掷OverflowException
时我会感到惊讶。考虑到这一点,我看到我的项目设置为x86
的编译,所以异常现在有意义 - Int64.MaxValue
太大而不适合32位IntPtr
。< / p>
checked
块。现在,我真的希望(IntPtr)ul
强制转换能够抛出异常,而且确实如此。
这让我想知道第一次演员是怎么回事。在未经检查的代码上使用ildasm
会产生以下结果:
IL_0000: nop
IL_0001: ldc.i4.m1
IL_0002: conv.i8
IL_0003: stloc.0
IL_0004: ldc.i8 0x7fffffffffffffff
IL_000d: stloc.1
IL_000e: ldloc.0
IL_000f: call native int [mscorlib]System.IntPtr::op_Explicit(int64)
IL_0014: stloc.2
IL_0015: ldloc.1
IL_0016: call native int [mscorlib]System.IntPtr::op_Explicit(int64)
所以-1放在堆栈上并转换为int64
,但没有额外的转换,从无符号到签名int64
。
checked
版本略有不同:
IL_0000: nop
IL_0001: nop
IL_0002: ldc.i4.m1
IL_0003: conv.i8
IL_0004: stloc.0
IL_0005: ldc.i8 0x7fffffffffffffff
IL_000e: stloc.1
IL_000f: ldloc.0
IL_0010: conv.ovf.i8.un
IL_0011: call native int [mscorlib]System.IntPtr::op_Explicit(int64)
IL_0016: stloc.2
IL_0017: ldloc.1
IL_0018: call native int [mscorlib]System.IntPtr::op_Explicit(int64)
现在有一个从unsigned到signed的转换,这在溢出的情况下是必要的。
不幸的是,这并没有回答原来的问题。
更新:我删除了不正确的答案部分,因此没有留下任何实际答案。但是,我希望它有用,所以我没有删除整个答案。
答案 1 :(得分:2)
IntPtr
和UIntPtr
类型只是地址的托管表示,地址本身就是一个数字。因此,它提供逻辑数字和相同签名/无符号值之间的转换。
在这种情况下,UIntPtr
是无符号的,因此仅提供对无符号数值(如ulong
)的转换。这与A
上接受long
(已签名)值的显式运算符不兼容。
您需要为ulong
添加额外的运算符,或者从long
UIntPtr
进行显式投射
A a = (A)(long)ul;