7
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?

following is 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 
Vince
  • 896
  • 5
  • 18
  • It might be interesting to inspect the IL generated by the above using ildasm to see if it treats IntPtr as a native int and hence performs the conversion using [conv.i](http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.conv_i%28v=vs.95%29.aspx) – Dan Bryant Apr 02 '13 at 18:35
  • @ Dan, I don't think so. any reference to "C# Language Specification"? – Vince Apr 03 '13 at 09:49
  • 1
    @Vince, I imagine this would be part of the ECMA-335(CLI spec) not ECMA-334(C# spec). IntPtr is considered a 'native int' data type. Which I am guessing being able to explicit cast between every built-in valuetype is in the spec. Something worth testing might be to see if this works in VB.Net and other .net languages. Along with testing to see if this works in monodevelop. That way you can more easily narrow down if this is C# specific or MS specific. I'm guessing this is part of the CLI spec somewhere though. – Will Apr 12 '13 at 20:59
  • thanks for your answer. we need more knowledge for correct answer. – Vince Apr 14 '13 at 16:50

2 Answers2

3

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

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. It did not. Imagine my surprise when the (IntPtr)l cast threw an 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.

Test #2: put a checked block around the same code.

Now, I really expected the (IntPtr)ul cast to throw an exception, and it did.

This made me wonder what was going on with the first cast. Using ildasm on the unchecked 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 put on the stack and converted to an int64, but there's no extra conversion from an unsigned to a 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 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.

Community
  • 1
  • 1
Joel Rondeau
  • 7,486
  • 2
  • 42
  • 54
  • I have a quick look your reference from Eric Lippert. But here it seems not suitable, becuase no standard conversion from ulong to long. and take a look at my sample class A. I defined a explicit operator as IntPtr, but A donesn't behave as IntPtr. Thanks for your reference. I'll take more time to study. – Vince Apr 02 '13 at 17:08
  • @Vince, there is an explicit standard conversion from ulong to long, and per Eric's answer, the explicit cast to IntPtr allows for the explicit conversion to be done. – Joel Rondeau Apr 02 '13 at 17:39
  • no. range for ulong is different from that for long. ulong.Max>long.Max ulong.Min>long.Min. – Vince Apr 02 '13 at 17:49
  • @Vince, range being different is exactly the reason why the conversion is explicit and not implicit. – Joel Rondeau Apr 02 '13 at 17:58
  • @Jole, from ulong to long(or from long to ulong) is explicit conversion but is not standard explicit conversion. refer "CSharp Language Specification". 6.3.2 says "if a standard implicit conversion exists from a type A to a type B, then a standard explicit conversion exists from type A to type B and from type B to type A." 6.3.1 lists which implicit conversions belong to standard implicit conversion, and 6.1.2 lists implicit numeric conversions belonging to standard implicit conversion. – Vince Apr 02 '13 at 18:07
  • @Vince, Yes, I'm seeing my mistake here. I suspect I'm very near the answer, but haven't actually found the answer. – Joel Rondeau Apr 02 '13 at 18:12
  • @Jole, still thanks for your answer. I'd like spend more time one studying C# specification.:) – Vince Apr 02 '13 at 18:14
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/27469/discussion-between-vince-and-joel-rondeau) – Vince Apr 03 '13 at 09:40
2

The IntPtr and UIntPtr types are just managed representations of an address which itself is a number. 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. This is incompatible with the explicit operator on A which accepts a long (signed) value.

You need to either add an extra operator for ulong or do an explicit cast to long from UIntPtr

A a = (A)(long)ul;
JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454
  • here is IntPtr instead of UIntPtr.:) I think it should only allow conversion from long to IntPtr. It's strange. – Vince Apr 02 '13 at 15:50