views:

48

answers:

3

System.Array serves as the base class for all arrays in the Common Language Runtime (CLR). According to this article:

For each concrete array type, [the] runtime adds three special methods: Get/Set/Address.

and indeed if I disassemble this C# code,

int[,] x = new int[1024,1024];
x[0,0] = 1;
x[1,1] = 2;
x[2,2] = 3;
Console.WriteLine(x[0,0]);
Console.WriteLine(x[1,1]);
Console.WriteLine(x[2,2]);

into CIL I get,

IL_0000:  ldc.i4     0x400
IL_0005:  ldc.i4     0x400
IL_000a:  newobj     instance void int32[0...,0...]::.ctor(int32,
                                                         int32)
IL_000f:  stloc.0
IL_0010:  ldloc.0
IL_0011:  ldc.i4.0
IL_0012:  ldc.i4.0
IL_0013:  ldc.i4.1
IL_0014:  call       instance void int32[0...,0...]::Set(int32,
                                                       int32,
                                                       int32)
IL_0019:  ldloc.0
IL_001a:  ldc.i4.1
IL_001b:  ldc.i4.1
IL_001c:  ldc.i4.2
IL_001d:  call       instance void int32[0...,0...]::Set(int32,
                                                       int32,
                                                       int32)
IL_0022:  ldloc.0
IL_0023:  ldc.i4.2
IL_0024:  ldc.i4.2
IL_0025:  ldc.i4.3
IL_0026:  call       instance void int32[0...,0...]::Set(int32,
                                                       int32,
                                                       int32)
IL_002b:  ldloc.0
IL_002c:  ldc.i4.0
IL_002d:  ldc.i4.0
IL_002e:  call       instance int32 int32[0...,0...]::Get(int32,
                                                        int32)
IL_0033:  call       void [mscorlib]System.Console::WriteLine(int32)
IL_0038:  ldloc.0
IL_0039:  ldc.i4.1
IL_003a:  ldc.i4.1
IL_003b:  call       instance int32 int32[0...,0...]::Get(int32,
                                                        int32)
IL_0040:  call       void [mscorlib]System.Console::WriteLine(int32)
IL_0045:  ldloc.0
IL_0046:  ldc.i4.2
IL_0047:  ldc.i4.2
IL_0048:  call       instance int32 int32[0...,0...]::Get(int32,
                                                        int32)
IL_004d:  call       void [mscorlib]System.Console::WriteLine(int32)

where the calls to the aforementioned Get and Set methods can be clearly seen. It seems the arity of these methods is related to the dimensionality of the array, which is presumably why they are created by the runtime and are not pre-declared. I couldn't locate any information about these methods on MSDN and their simple names makes them resistant to Googling. I'm writing a compiler for a language which supports multidimensional arrays, so I'd like to find some official documentation about these methods, under what conditions I can expect them to exist and what I can expect their signatures to be.

In particular, I'd like to know whether its possible to get a MethodInfo object for Get or Set for use with Reflection.Emit without having to create an instance of the array with correct type and dimensionality on which to reflect, as is done in the linked example.

+1  A: 

I'm not sure if it will address your very specific question but a great text on the subject (among others) is CLR via C#. It gets very in-depth for many of the topics you're interested in and spends a lot of time with the disassembler looking at the inner workings of many base .NET types including arrays. Definitely worth checking out.

Paul Sasik
Thanks for reminding me to look at CLR via C#, which I do have here. I just read through Chapter 13: Arrays but I can't see any mention of the Get, Set and Address methods.
Rob Smallshire
+2  A: 

Look here, specifically section 14.2 on pages 63-65

http://download.microsoft.com/download/7/3/3/733AD403-90B2-4064-A81E-01035A7FE13C/MS%20Partition%20II.pdf

But the takeaway, and you can tell from the IL, is that they're the getter and setter methods for dealing with arrays at the given index positions.

• A Get method that takes a sequence of int32 arguments, one for each dimension of the array, and returns a value whose type is the element type of the array. This method is used to access a specific element of the array where the arguments specify the index into each dimension, beginning with the first, of the element to be returned.

• A Set method that takes a sequence of int32 arguments, one for each dimension of the array, followed by a value whose type is the element type of the array. The return type of Set is void. This method is used to set a specific element of the array where the arguments specify the index into each dimension, beginning with the first, of the element to be set and the final argument specifies the value to be stored into the target element.

• An Address method that takes a sequence of int32 arguments, one for each dimension of the array, and has a return type that is a managed pointer to the array’s element type. This method is used to return a managed pointer to a specific element of the array where the arguments specify the index into each dimension, beginning with the first, of the element whose address is to be returned.

Edit: That's pages 63-65 using the document's page numbering. 73-75 in the actual PDF.

Anthony Pegram
Excellent! That's exactly what I was looking for. Thank you.
Rob Smallshire
+1  A: 

To answer your second question, you don't need to create an instance to get a MethodInfo for these methods. Something like

var mi = typeof(string).MakeArrayType(6).GetMethod("Get");

will work to get the Get method for the string[,,,,,] type.

kvb
Splendid! Now I have everything I came for.
Rob Smallshire
I've subsequently discovered that care is required when selecting between the overloads of MakeArrayType. With no arguments - MakeArrayType() creates a vector type such as string[]. With an argument of one - MakeArrayType(1) creates a multidimensional array with one dimension of type string[*]. These types are not compatible. For interoperability with the other .NET languages this means that when rank == 1 you will need to special-case the call to the parameterless method.
Rob Smallshire