Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is "new int[n] is object[]" false? And why is "int[] is object[] arr" a pattern error?

Tags:

c#

Why is an array of ints not an array of objects? And why can a pattern of type 'object[]' not be used for 'int[]'?

    1 is object
    True
    new int[10] is object
    True
    new int[10] is object[]         // Why?
    False
    (Array)new int[10] is object[]
    False
    (Array)new object[10] is object[]
    True
    new object() is object
    True
    new object[10] is object
    True
    new object[10] is object[]
    True

    int[] arr = new int[10];
    // Why the compilation error?
    // error CS8121: An expression of type 'int[]' cannot be handled by a pattern of type 'object[]'
    if (arr is object[] objArr)   
        Console.WriteLine(objArr);
    // And this works:        
    if ((Array)arr is object[] objArr)
        Console.WriteLine(objArr);

I came across this line in the source code: https://source.dot.net/#System.Private.CoreLib/Array.cs,1644

.NET version is 3.1.10 (same on mono 6.4.0).

like image 732
Artyom Avatar asked Dec 31 '22 06:12

Artyom


2 Answers

Actually (if I understand the problem correctly), in this case, it's because value types are not covariant:

(Array)new string[10] is object[] 
true

(Array)new int[10] is object[]
false

The CLR disallows it because it needs to preserve the identity, where as boxing will not, it fundamentally changes the type in memory

Eric Lippert has a great blog post about identity at Representation and identity:

covariant and contravariant conversions of interface and delegate types require that all varying type arguments be of reference types. To ensure that a variant reference conversion is always identity-preserving, all of the conversions involving type arguments must also be identity-preserving. The easiest way to ensure that all the non-trivial conversions on type arguments are identity-preserving is to restrict them to be reference conversions.

like image 104
TheGeneral Avatar answered Jan 02 '23 20:01

TheGeneral


Let´s make an example that contains lists instead of arrays, because the same semantics apply.

Assume you have a list of base-class Animal. If you could cast List<Giraffe> to List<Animal>, you can do this also:

((List<Animal> myGiraffes).Add(new Lion())

which surely breaks your concept of type-safety. That´s why List<T> is not co-variant. In your case you have an array, which - by error or design - however is co-variant. Thus you can convert an array of any type to an array of type object.

object[] a = new string[0];

Also have a look at Why are arrays covariant but generics are invariant?. In short arrays exist far longer in the language-concepts of C# then generics and co-variance.

like image 27
MakePeaceGreatAgain Avatar answered Jan 02 '23 21:01

MakePeaceGreatAgain