IntPtr allows 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 does IntPtr allow behavior?

following IL code:

 .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 
+7
source share
2 answers

I agree that this seemed a little strange, so I did some tests.

Test No. 1: Make a listing of oolong and long

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

Since cast IntPtr states that it can throw an OverflowException , I expected that casting (IntPtr)ul would throw an exception. This is not true. Imagine my surprise when a throw (IntPtr)l threw an OverflowException . Having looked at this, I saw that my project was configured to compile for x86 , so now it became clear that this exception is Int64.MaxValue too large to fit into 32-bit IntPtr .

Test # 2: place the checked block around the same code.

Now I really expected that casting (IntPtr)ul would throw an exception, and that happened.

It made me think about what happened to the first actor. Using ildasm for untested code leads to the following:

 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 int64 stack and converted to int64 , but there is no extra conversion from unsigned to signed int64 .

The checked version is slightly different:

 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.

Unfortunately, this does not answer the original question.

Update: I deleted the part of the answer that was incorrect and did not leave any real answer. However, I expect this to be useful, so I have not deleted the whole answer.

+3
source

The IntPtr and UIntPtr types are simply managed representations of an address, which itself is a number. Therefore, it provides conversions between values ​​that are logically numeric and the same signed / unsigned.

In this case, UIntPtr is unsigned and therefore provides only conversions of unsigned numerical values ​​such as ulong . This is incompatible with an explicit operator on A that takes the value long (signed).

You need to either add an additional operator for ulong , or do an explicit conversion to long from UIntPtr

 A a = (A)(long)ul; 
+2
source

All Articles