Of course, C# supports unidimensional and multidimensional arrays, and supports arrays of arrays. Famously, if, in an array of arrays, the inner array type has a different rank (dimension count) than the outer array, then the C# language and the .NET runtime disagree on the order in which the brackets should be given in the type name. In particular:
typeof(string[][,]).Name == "String[,][]"
and:
typeof(string[,][]).Name == "String[][,]"
so some confusion can arise, but it works.
Here is a correct use of such types:
static void Foo1(string[,][] a) {
//read:
string[] entry = a[7, 8];
//write:
a[7, 8] = new string[] { "alfa", "bravo", "charlie", "delta", };
}
static void Bar1(string[][,] a) {
//read:
string[,] entry = a[9];
//write:
a[9] = new string[,] { { "echo", "foxtrot", }, { "golf", "hotel", }, { "india", "juliett", }, };
}
The readers can verify for themselves that this can be compiled and works (must pass in sufficiently large array of arrays, of course).
Now serious issues arise when we try to combine the above with nullable reference types, as present in C# since C# 8 (from 2019). At least with the compiler implementation I have here (newest version of Visual Studio 2022). Namely if I make this slight modification:
// nullable reference types must be enabled, for example with:
#nullable enable
static void Foo2(string[,]?[] a) {
//read:
string[]? entry = a[7, 8];
//write:
a[7, 8] = new string[] { "alfa", "bravo", "charlie", "delta", };
}
static void Bar2(string[]?[,] a) {
//read:
string[,]? entry = a[9];
//write:
a[9] = new string[,] { { "echo", "foxtrot", }, { "golf", "hotel", }, { "india", "juliett", }, };
}
then I get all sorts of wrong compile-time errors near [7, 8]
and [9]
, like:
Error CS0022 Wrong number of indices inside [];
But clearly the index counts are correct and in agreements of the ranks, just as they were before without the ?
.
Is it a known bug that the combination of arrays of arrays and nullable references type breaks everything completely?
I believe this is related to the thread Multidimensional arrays, nullable reference types and type conversion.
PS! If you list the brackets in reverse order in the type (for the declaration of parameter a
), it compiles!
Update: Another example. If you create a member (like a field or something) whose type is declared with the C# syntax:
string[][,][,,][,,,][,,,,][,,,,,] // C#
so six brackets, then the IL created has the type which .NET calls:
String[,,,,,][,,,,][,,,][,,][,][] // IL disassembly
which is correct since C# 1.0 and .NET Framework 1. But if you put a nullable mark in the middle, so declare the type in C# as:
string[][,][,,]?[,,,][,,,,][,,,,,] // C#
then the IL member has the type:
String[,,][,][][,,,,,][,,,,][,,,] // IL disassembly
Dictionary
with struct key and string array or jagged value. – DuctileType.Name
uses a different rank specifier order than the c# compiler. – Koa