Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does "int[] is uint[] == true" in C#

Can somebody clarify the C# is keyword please. In particular these 2 questions:

Q1) line 5; Why does this return true?

Q2) line 7; Why no cast exception?

public void Test() {     object intArray = new int[] { -100, -200 };                  if (intArray is uint[]) //why does this return true?     {         uint[] uintArray = (uint[])intArray; //why no class cast exception?          for (int x = 0; x < uintArray.Length; x++)         {             Console.Out.WriteLine(uintArray[x]);         }     } } 

MSDN's description does not clarify the situation. It states that is will return true if either of these conditions are met. (http://msdn.microsoft.com/en-us/library/scekt9xw(VS.71).aspx>MDSN Article)

 expression is not null. expression can be cast to type. 

I don't believe that you can do a valid cast of int[] into uint[]. Because:

A) This code does not compile:

int[] signed = new int[] { -100 }; uint[] unsigned = (uint[])signed;  

B) Doing the cast in the debugger gives an error:

(uint[])signed "Cannot convert type 'int[]' to 'uint[]'" 

Sure enough, if line 3 was int[] instead of object then it would never compile. Which brings me to a final question related to Q2.

Q3) Why does C# raise a cast/conversion error in the debugger and compiler but not at runtime?

like image 950
Peervm Avatar asked Feb 27 '09 06:02

Peervm


2 Answers

C# and the CLR have somewhat different conversion rules.

You can't directly cast between int[] and uint[] in C# because the language doesn't believe any conversion is available. However, if you go via object the result is up to the CLI. From the CLI spec section 8.7 (I hope - I'm quoting an email exchange I had on this topic with Eric Lippert a while ago):

Signed and unsigned integral primitive types can be assigned to each other; e.g., int8 := uint8 is valid. For this purpose, bool shall be considered compatible with uint8 and vice versa, which makes bool := uint8 valid, and vice versa. This is also true for arrays of signed and unsigned integral primitive types of the same size; e.g., int32[] := uint32[] is valid.

(I haven't checked, but I assume that this sort of reference type conversion being valid is what makes is return true as well.)

It's somewhat unfortunate that there are disconnects between the language and the underlying execution engine, but it's pretty much unavoidable in the long run, I suspect. There are a few other cases like this, but the good news is that they rarely seem to cause significant harm.

EDIT: As Marc deleted his answer, I've linked to the full mail from Eric, as posted to the C# newsgroup.

like image 123
Jon Skeet Avatar answered Sep 20 '22 23:09

Jon Skeet


Now that's interesting. I found this in the ECMA-335 standard. 4.3 castclass. Note that:

  • Arrays inherit from System.Array.

  • If Foo can be cast to Bar, then Foo[] can be cast to Bar[].

  • For the purposes of note 2 above, enums are treated as their underlying type: thus E1[] can be cast to E2[] if E1 and E2 share an underlying type.

You can cast int to uint, but that it behaves like this is very strange. Visual Studio does not recognize any of this, even the watch, when the debugger is attached just shows a question mark '?'.

You might wanna take a look at this, fast forward about 10 minutes in and listen to Anders explain the co-variant array implementation. I think that is the fundamentally underlying issue here.

like image 23
John Leidegren Avatar answered Sep 18 '22 23:09

John Leidegren