IntPtr允许从ulong到long的隐式转换
本文关键字:转换 long ulong IntPtr | 更新日期: 2023-09-27 18:06:59
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
我同意这看起来有点奇怪,所以我运行了几个测试。
测试#1:做一个long和long的cast
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
。
测试#2:在相同的代码周围放置一个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的强制转换,这在溢出的情况下是必要的。
不幸的是,这并没有回答最初的问题。
更新:我删除了答案中不正确的部分,因此没有留下实际答案。但是,我希望它是有帮助的,所以我没有删除整个答案。
IntPtr
和UIntPtr
类型只是地址的托管表示,它本身是一个数字。因此,它提供了逻辑数字和具有相同符号/无符号的值之间的转换。
在这种情况下,UIntPtr
是无符号的,因此只提供到像ulong
这样的无符号数值的转换。这与A
上的显式运算符不兼容,后者接受long
(带符号)值。
您需要为ulong
添加一个额外的操作符,或者从UIntPtr
显式强制转换为long
A a = (A)(long)ul;