为什么 C# 中的锯齿状数组以相反的方式定义?

Why are jagged arrays in C# defined in the opposite way?

假设我有一个名为 T 的类型。现在假设我制作了一个 T 类型的数组,它给了我 T[]。在代码中,它给出:

var myArray = new T[10];

长度为 10。因此,我们可以看到这构成了一个包含 10 个 T 类型项的数组。这适用于 TintstringBinaryWriter 或任何标准。但是假设 Tarray 类型,例如 int[]string[].

然后,如果我们要定义一个包含 10 个 Tint[])类型项的数组,它应该给出如下内容:

var myArray = new int[][10];

通过将上一个示例中的 T 替换为 int[]。但这会产生语法错误,因为 C# 中正确的语法是:

var myArray = new int[10][];

如果我们遵循第一个示例的逻辑,应该给出一个包含 未定义数量 的包含 10 个整数的数组。

这同样适用于锯齿状数组的任何维度:

var myArray = new int[][][10];

是错误的,因为语法上正确的方式是:

var myArray = new int[10][][];

这不是个人偏好或代码风格的争论,而是简单的逻辑:为什么我们定义 数组类型 的数组时的语法与定义数组时的语法不同还有什么吗?

new T[10] 表示您想要一个包含 10 个 T 类型项的数组。它与 new int[][10] 不同,因为它的行为不像其他语言中的预处理器宏。它不只是将 T 与要创建的任何类型交换 new int[][10]。相反,它与 T 始终是内部类型并且 new T[10] 定义外部数组的大小这一事实一致。

因此,如果 T 是 DateTime,那么您会期望有 10 个日期时间并访问它们,例如 myArray[9].Day

如果每个项目 T 都是一个 int[] 那么您仍然总是索引到包含 10 个项目的数组,然后访问项目 T,它是一个数组:myarray[9][2465] 假设内部数组在索引 9 处至少有 2466 个项目。

所以外层数组的索引总是排在第一位。

A T[10] 是大小为 10 的项目 T 数组。无论 T 是内部项目。

你说你的期望是 new T[10] 会产生 var myArray = new int[][10];。然而,即使在 C++ 中声明多维数组时,外部维度在声明中排在第一位:

https://www.tutorialspoint.com/cplusplus/cpp_multi_dimensional_arrays.htm

在 C++ 中声明交错数组与 C++ 类似,只是您必须使用指针语法并首先声明外部数组,然后分别初始化每个内部数组。

让我们根据每个语句所说的语义来考虑它:

A) new T[10]:我想要一个外部维度为 10 的数组,每个元素包含一个类型 T

B) new T[10] 其中 Tint[]:我想要一个外部维度为 10 的数组,每个元素包含一个长度未知的 int 数组。

C) new int[][10]: 我想要一个外维度未知的数组,每个元素包含一个包含 10 个整数的数组。

D) new int[10][]: 我想要一个外维为10的数组,每个元素包含一个长度未知的int数组。

注意 B 和 D 匹配,但 B 和 C 不匹配。

这样,初始化语法就反映了访问语法。

var myArray = new int[NUM_ROWS][];
...
/* Initialize rows of myArray */
...
var x = myArray[0][5]; /* Should this be row 0, col 5; or row 5, col 0? */

myArray[0][5] 解析为 (myArray[5])[0] 是违反直觉的,因为索引将从左到右反转,因此我们将 myArray[0][5] 解析为 (myArray[0])[5].

因此,在myArray[i][j]中,i对应二维数组的索引,j对应一维数组的索引myArray[i].

现在,i的范围可以从0NUM_ROWS-1,所以它与初始化var myArray = new int[NUM_ROWS][]的访问语法是对称的。这样,左边一组括号仍然对应二维数组的长度,右边一组括号仍然对应一维数组

编辑: 我看到有人在评论中发帖 a link to an article where Eric Lippert goes through a more elaborate example and justification