Multidimensional arrays are behind the scenes just normal flattened arrays with some additional index calculation. So they store the size of each dimension which is used whenever you read or write. I’m sure this “extra code” (which is most likely inlined in the final build) can’t be properly debugged.
Have a look at this example:
public class MultiDimArrayDebug : MonoBehaviour
{
bool[,] testArray;
bool[] normalArray;
void Start ()
{
testArray = new bool[7, 7];
testArray[3, 3] = true;
normalArray = new bool[49];
normalArray[3 + 3 * 7] = true;
}
}
The Start method when it’s compiled to IL looks like this:
.method private hidebysig instance void Start () cil managed
{
.maxstack 8
IL_0000: nop
IL_0001: ldarg.0
IL_0002: ldc.i4.7
IL_0003: ldc.i4.7
IL_0004: newobj instance void bool[0..., 0...]::.ctor(int32, int32)
IL_0009: stfld bool[0..., 0...] MultiDimArrayDebug::testArray
IL_000e: ldarg.0
IL_000f: ldfld bool[0..., 0...] MultiDimArrayDebug::testArray
IL_0014: ldc.i4.3
IL_0015: ldc.i4.3
IL_0016: ldc.i4.1
IL_0017: call instance void bool[0..., 0...]::Set(int32, int32, bool)
IL_001c: ldarg.0
IL_001d: ldc.i4.s 49
IL_001f: newarr [mscorlib]System.Boolean
IL_0024: stfld bool[] MultiDimArrayDebug::normalArray
IL_0029: ldarg.0
IL_002a: ldfld bool[] MultiDimArrayDebug::normalArray
IL_002f: ldc.i4 24
IL_0034: ldc.i4.1
IL_0035: stelem.i1
IL_0036: ret
} // end of method MultiDimArrayDebug::Start
0002 to 0004 is actually a call to constructor which creates the multidimensional array.
0001 + 0009 store the resulting object in the field of my testing class
000e +000f load the reference to the array back on the stack
0014 to 0017 call the Set method of the multidimensional array
So setting the value of a multidimensional array actually invokes a method which performs the setting of the value. This method is auto generated by the framework. Now have a look at a “normal” one dimensional array:
001c to 0024 creates a new bool array and stores it in the field
0029 + 002a loads the reference from the field back on the stack
002f to 0035 sets the array element to “true” (1).
As you can see normal / one dimensional arrays have native support. The creation as well as the read and write of elements are actually opcodes (newarr–> new array, stelem → store element ). However multidimensional arrays are actually framework generated wrapper classes around a normal native array. How those are actually implemented is an implementation detail of the framework. Those wrapper classes do not actually exist anywhere as there would be a countless amount of them. Keep in mind that you can create an “n” dimensional array where n can go up to “32”.
Some more information on debugging
When you debug the example code i’ve posted you have to keep in mind that the code that is actually executed is the IL code (which is actually JIT compiled to native code) and not the C# source code. The debugger has to match up the actual emitted opcodes with the source code line. This can easily go wrong, especially when different compilers are involved.
VisualStudio somehow executes those two lines:
testArray = new bool[7, 7];
testArray[3, 3] = true;
at once instead of one after another. So it seems VS as trouble to actually keep the code and source in sync. However if you place a breakpoint on each line and instead of “Step over” or “Step into” you just use “continue” (F5) it will properly execute line by line.