简体   繁体   English

IntPtr允许从ulong到long的隐式转换

[英]IntPtr allow implicit conversion from ulong to long

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'

why do IntPtr allow the behavior? 为什么IntPtr允许这种行为?

following is IL code: 以下是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 

I agree that this seemed a bit odd, so I ran a couple of tests. 我同意这看起来有点奇怪,所以我进行了几次测试。

Test #1: Do a cast of a ulong and a long 测试#1:做一个ulong和long的演员

ulong ul = UInt64.MaxValue;
long l = Int64.MaxValue;
IntPtr ulptr = (IntPtr)ul;
IntPtr lptr = (IntPtr)l;

Because the IntPtr cast states that it may throw an OverflowException , I was expecting the (IntPtr)ul cast to throw an exception. 因为IntPtr声明它可能抛出OverflowException ,所以我期待(IntPtr)ul抛出异常。 It did not. 它没。 Imagine my surprise when the (IntPtr)l cast threw an OverflowException . 想象一下,当(IntPtr)l cast抛出OverflowException时我会感到惊讶。 Looking into this, I saw that my project was set to compile for x86 , so the exception now made sense -- Int64.MaxValue is too large to fit into a 32-bit IntPtr . 看看这个,我看到我的项目设置为x86编译,所以异常现在有意义 - Int64.MaxValue太大Int64.MaxValue适合32位IntPtr

Test #2: put a checked block around the same code. 测试#2:在相同的代码周围放置一个checked块。

Now, I really expected the (IntPtr)ul cast to throw an exception, and it did. 现在,我真的期望(IntPtr)ul抛出异常,它确实如此。

This made me wonder what was going on with the first cast. 这让我想知道第一次演员是怎么回事。 Using ildasm on the unchecked code leads to the following: 在未经检查的代码上使用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)

So -1 is put on the stack and converted to an int64 , but there's no extra conversion from an unsigned to a signed int64 . 所以-1被放在堆栈上并转换为int64 ,但是没有从unsigned到signed int64额外转换。

The checked version is slightly different: 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)

Now there is a cast from unsigned to signed, which is necessary in case of overflow. 现在有一个从unsigned到signed的转换,这在溢出的情况下是必要的。

Unfortunately, this doesn't answer the original question. 不幸的是,这并没有回答原来的问题。

Update: I removed the portion of the answer that was incorrect, thus leaving no actual answer. 更新:我删除了不正确的答案部分,因此没有留下真正的答案。 However, I expect it is helpful so I have not deleted the entire answer. 但是,我希望它有用,所以我没有删除整个答案。

The IntPtr and UIntPtr types are just managed representations of an address which itself is a number. IntPtrUIntPtr类型只是地址的托管表示,地址本身就是一个数字。 Hence it provides conversions between values which are logically numeric and of the same signed / unsignedness. 因此,它提供逻辑数字和相同签名/无符号值之间的转换。

In this case UIntPtr is unsigned and hence only provides conversions to unsigned numeric values like ulong . 在这种情况下, UIntPtr是无符号的,因此只提供无符号数值(如ulong转换。 This is incompatible with the explicit operator on A which accepts a long (signed) value. 这与接受long (签名)值的A上的显式运算符不兼容。

You need to either add an extra operator for ulong or do an explicit cast to long from UIntPtr 您需要可以添加额外的运营商ulong或做一个明确的转换,以longUIntPtr

A a = (A)(long)ul;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM