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

前端 未结 5 1109
死守一世寂寞
死守一世寂寞 2020-11-27 05:12

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 e

相关标签:
5条回答
  • 2020-11-27 05:44

    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.

    0 讨论(0)
  • 2020-11-27 05:56

    I'm guessing backwards compatablility with .NET 1: I'm still a bit fuzzy about the details, but I beleive the CLR type of all arrays is simply System.Array, with extra Type properties to lookup the element type. 'is' probably just didn't account for that in CLR v1, and now must maintain that.

    It not working in the (uint[])(new int[]{}) case is probably due to the C# compiler (not the CLR runtime) being able to do stricter typechecking.

    Also, arrays are just type unsafe in general:

    Animal[] tigers = new Tiger[10];
    tigers[3] = new Elephant(); // ArrayTypeMismatchException
    
    0 讨论(0)
  • 2020-11-27 06:02

    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.

    0 讨论(0)
  • 2020-11-27 06:03

    OK,

    I attempt to take a stab at this.

    First off, the documentation says, "Checks if an object is compatible with a given type.", It also says that IF the type on the left is "castable" (you can convert without exception) to the type on the right, and the expression evaluates to non-null, the "is" keyword will evaluate to true.

    Look to Jon Skeet for the other answer. He said it more eloquently than I could. He's right, if a conversion is available, it will accept your syntax, you could subsequently write your own, but it would seem overkill in this situation.

    Reference: http://msdn.microsoft.com/en-us/library/scekt9xw(VS.80).aspx

    0 讨论(0)
  • 2020-11-27 06:06

    Suggestion:

    Declaring intArray as "int [] intArray" rather then "object intArray" will allow the compiler to pick up the invalid C# cast. Unless you absolutely have to use object, I would take that approach.

    Re Q2,Q3:

    At runtime have you tried wrapping the cast in a checked block?

    From this article at MSDN:

    By default, an expression that contains only constant values causes a compiler error if the expression produces a value that is outside the range of the destination type. If the expression contains one or more non-constant values, the compiler does not detect the overflow.

    ...

    By default, these non-constant expressions are not checked for overflow at run time either, and they do not raise overflow exceptions. The previous example displays -2,147,483,639 as the sum of two positive integers.

    Overflow checking can be enabled by compiler options, environment configuration, or use of the checked keyword.

    As it says, you can enforce overflow checking more globally via a compiler setting or environment config.

    In your case this is probably desirable as it will cause a runtime error to be thrown that will ensure the likely invalid unsigned number to signed number overflow will not occur silently.

    [Update] After testing this code, I found that using a declaration of type object instead of int [] appears to bypass the standard C# casting sytax, regardless of whether checked is enabled or not.

    As JS has said, when you use object, you are bound by CLI rules and these apparently allow this to occur.

    Re Q1:

    This is related to the above. In short, because the cast involved it does not throw an exception (based on current overflow setting). Whether this is a good idea is another question.

    From MSDN:

    An "is" expression evaluates to true if the provided expression is non-null, and the provided object can be cast to the provided type without causing an exception to be thrown.

    0 讨论(0)
提交回复
热议问题