What's the magic of arrays in C#
Asked Answered
A

8

24
int[] a = new int[5];
string[] b = new string[1];

The types of both a and b inherit from the abstract System.Array, but there is no real classes in the built-in library(it seems that there are some runtime types, you can't find the type defination class of an int[]). Can you tell me what happens while compiling? And why did they(the c# team) make this design(I mean why it's not something like Array<T>,instead they are using an abstract class with compiler magics)?

Attestation answered 20/12, 2010 at 12:35 Comment(3)
An interesting question, but I would imagine the CLR's magic contribution is more than the C# compiler's.Chanson
What do you mean by "there is no such a real class"?Culbreth
@BoltClock: You can't find the source code of this class, can you? I mean the real type, not System.Array. Sorry for my poor English.Attestation
I
21

Trying to reason this out within the .NET type system doesn't get you very far. There is core support built into the JIT compiler and the CLR to deal with creating arrays. A statement like this:

        var arr = new int[5];

Generates this IL:

  IL_0001:  ldc.i4.5
  IL_0002:  newarr     [mscorlib]System.Int32

Which the JIT compiler then translate into this machine code:

00000035  mov         edx,5                 ; arg2 = array size
0000003a  mov         ecx,6F535F06h         ; arg1 = typeof(int)
0000003f  call        FFD52128              ; call JIT_NewArr1(type, size)

Core ingredients here are the dedicated IL opcode, newarr, instead of the usual newobj opcode that creates an instance of a class. And the simple translation to a CLR helper function that actually gets the object created. You can have a look-see at this helper function with the SSCLI20 source code, clr\src\vm\jithelpers.cpp. Too large to post here, but it is heavily optimized to make this kind of code run as fast possible, having direct access to the type internals available to CLR code.

There are two of these helpers available, JIT_NewArr1() creates one-dimensional (vector) arrays and JIT_NewMDArr() creates multi-dimensional arrays. Compare to the two overloads available for Type.MakeArrayType().

Insane answered 20/12, 2010 at 13:53 Comment(1)
Yeah, but there is no reason the compiler couldn’t still use the newarr IL opcode even if the class were called Array<T> instead of T[] in C#. The real answer, of course, is that C# 1.0 didn’t have generics, but it certainly needed arrays.Hsu
L
10

And why did they(the c# team) make this design(I mean why it's not something like Array...

Generics are ideal for defining a container, as they constrain the element type so you can't insert type A and try to retrieve type B.

But generics were not added until CLR2/C#2. So arrays had to provide type safety in their own way.

Even so, it's not that different to generics. You note that there is no special class for int[]. But nor would there be for Array<int>. In generics there would only be the generic class Array<T>, and the CLR "magically" creates specialised versions for distinct type argument you use. So it would be no less "magic" if generics were used.

Despite this, in the CLR the type of any object is reified (it exists as a value you can manipulate), of type Type, and can be obtained with typeof. So although there is no code declaration of any array type (and why would you need to see it?) there is a Type object that you can query.

By the way, there was a design flaw in the way arrays constrain element types. You can declare an array:

int[] ints = ...

You can then store it in a looser variable:

object[] objs = ints;

But that means you can insert a string (at least it appears so at compile time):

objs[3] = "Oh dear";

At runtime it throws an exception. The idea of static type checking is to catch this kind of thing at compile time, not runtime. Generics would not have had this problem because they don't give assignment compatibility to generic class instances based on the compatibility of their type parameters. (Since C#4/CLR4 they have gained the ability to do that where it makes sense, but that wouldn't make sense for a mutable array.)

Lag answered 20/12, 2010 at 12:59 Comment(1)
+1. Amazing none of the other answers mentioned the real answer, which is that C# 1.0 didn’t have generics.Hsu
L
7

Look at the Array class.

When declaring an array using the [] syntax, the compiler, behind the scenes will use this class for you.

For C#, [] becomes a type that inherits from System.Array.

From the C# 4.0 spec:

§12.1.1 The System.Array type

The type System.Array is the abstract base type of all array types. An implicit reference conversion (§6.1.6) exists from any array type to System.Array, and an explicit reference conversion (§6.2.4) exists from System.Array to any array type. Note that System.Array is not itself an array-type. Rather, it is a class-type from which all array-types are derived.

Limewater answered 20/12, 2010 at 12:38 Comment(2)
I guess what OP is asking is how objects are created from Array seeing as it's an abstract class.Culbreth
No, it isn't syntactic sugar for using Array; there is no "unsugared" syntax.Explicate
P
2

There is such class. You cannot inherit it, but when you write "int[]" the compiler creates a type that inherits System.Array. So if you declare a variable:

int[] x;

This variable will have a type that inherits System.Array, and therefore has all its methods and properties.

This is also similar to delegates. When you define a delegate:

delegate void Foo(int x);
delegate int Bar(double x);

Then the type Foo is actually a class that inherits System.MulticastDelegate and Bar is a class that inherits System.Delegate.

Patino answered 20/12, 2010 at 12:42 Comment(3)
I know that. How can they make it?Attestation
Oh.. I get you now. They can do it because compilers and the CLR are allowed to do things that you, as a programmer, is not allowed to do. For example, the c# compiler will not allow you to inherit System.Delegate but the compiler itself has no problem doing so. It could allow you to do so too, but the c# language designers decided that it will not. In a similar way, the CLR is allowed to generate types that inherit System.Array, but it doesn't allow you, as the programmer, to do it.Patino
@Hsu Can you justify this claim? The compiler / JIT are effectively creating a subtype of System.Array, and other posters such as Oded have pointed this same thing out.Botha
F
2

I would recommend getting the ECMA 335 spec and looking for Arrays if you want to know the low level detail: http://www.ecma-international.org/publications/standards/Ecma-335.htm

Fossa answered 20/12, 2010 at 12:53 Comment(0)
B
1

I went digging through the ECMA 335 spec, so I figured I'd share what I read.

Exact array types are created automatically by the VES when they are required. Hence, the operations on an array type are defined by the CTS. These generally are: allocating the array based on size and lower-bound information, indexing the array to read and write a value, computing the address of an element of the array (a managed pointer), and querying for the rank, bounds, and the total number of values stored in the array.

The VES creates one array type for each distinguishable array type.

Vectors are subtypes of System.Array, an abstract class pre-defined by the CLI. It provides several methods that can be applied to all vectors. See Partition IV.

While vectors (§II.14.1) have direct support through CIL instructions, all other arrays are supported by the VES by creating subtypes of the abstract class System.Array (see Partition IV)

While vectors (§II.14.1) have direct support through CIL instructions, all other arrays are supported by the VES by creating subtypes of the abstract class System.Array (see Partition IV)

The class that the VES creates for arrays contains several methods whose implementation is supplied by the VES:

It goes on to state, quite verbosely, that the methods supplied are:

  1. Two constructors
  2. Get
  3. Set
  4. Address (returns a managed pointer)

VES means Virtual Execution System, and the CLR is an implementation of it.

The spec also details how to store the data of the array (contiguously in row-major order), what indexing is allowed in arrays (0-based only), when a vector is created (single dimensional, 0-based arrays) as opposed to a different array type, when the CIL instruction newarr is used as opposed to newobj (creating a 0-based, single dimensional array).

Basically everything that the compiler has to do to build the method lookup tables etc.. for a regular type, it has to do for arrays, but they just programmed a more versatile and slightly special behavior into the compiler / JIT.

Why did they do it? Probably because arrays are special, widely used, and can be stored in an optimized fashion. The C# team did not necessarily make this decision though. It's more of a .NET thing, which is a cousin to Mono and Portable.NET, all of which are a CIL thing.

Botha answered 11/3, 2015 at 2:1 Comment(0)
C
0

Arrays are special to CLR. They're allocated with 'newarr' instruction, and elements are accessed with 'ldelem*' and 'stelem*' instructions, not via System.Array methods;

see http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.newarr.aspx

You can check out ildasm output to see how arrays are represented.

So, to answer your question - no new type declaration is generated for any particular array.

Chloride answered 20/12, 2010 at 12:46 Comment(0)
M
0

[] is a syntax(syntatic sugar) for defining Arrays in c#. Maybe CreateInstance will be replaced at runtime

 Array a = Array.CreateInstance(typeof(int), 5); 

is same as

int[] a = new int[5];

Source for CreateInstance (taken from reflector)

public static unsafe Array CreateInstance(Type elementType, int length)
{
    if (elementType == null)
    {
        throw new ArgumentNullException("elementType");
    }
    RuntimeType underlyingSystemType = elementType.UnderlyingSystemType as RuntimeType;
    if (underlyingSystemType == null)
    {
        throw new ArgumentException(Environment.GetResourceString("Arg_MustBeType"), "elementType");
    }
    if (length < 0)
    {
        throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
    }
    return InternalCreate((void*) underlyingSystemType.TypeHandle.Value, 1, &length, null);
}
Manning answered 20/12, 2010 at 13:11 Comment(3)
It is not the same. The first one compiles into a call instruction to Array.CreateInstance, the second into a newarr instruction. Also, the type of the local variable is different.Hsu
@Timwi, I'd bet that Array.CreateInstance calls newarr.Botha
@MillieSmith: Sure, somewhere down the line, the newarr instruction will be used. But the two snippets of code are not the same (not equivalent).Hsu

© 2022 - 2024 — McMap. All rights reserved.