int[] a = new int[5];
string[] b = new string[1];
The types of both a and b inherit from the abstract System.Array<
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().