为什么 C# 中的锯齿状数组以相反的方式定义?
Why are jagged arrays in C# defined in the opposite way?
假设我有一个名为 T
的类型。现在假设我制作了一个 T
类型的数组,它给了我 T[]
。在代码中,它给出:
var myArray = new T[10];
长度为 10。因此,我们可以看到这构成了一个包含 10 个 T
类型项的数组。这适用于 T
是 int
、string
、BinaryWriter
或任何标准。但是假设 T
是 array 类型,例如 int[]
或 string[]
.
然后,如果我们要定义一个包含 10 个 T
(int[]
)类型项的数组,它应该给出如下内容:
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]
其中 T
是 int[]
:我想要一个外部维度为 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
的范围可以从0
到NUM_ROWS-1
,所以它与初始化var myArray = new int[NUM_ROWS][]
的访问语法是对称的。这样,左边一组括号仍然对应二维数组的长度,右边一组括号仍然对应一维数组
编辑: 我看到有人在评论中发帖 a link to an article where Eric Lippert goes through a more elaborate example and justification。
假设我有一个名为 T
的类型。现在假设我制作了一个 T
类型的数组,它给了我 T[]
。在代码中,它给出:
var myArray = new T[10];
长度为 10。因此,我们可以看到这构成了一个包含 10 个 T
类型项的数组。这适用于 T
是 int
、string
、BinaryWriter
或任何标准。但是假设 T
是 array 类型,例如 int[]
或 string[]
.
然后,如果我们要定义一个包含 10 个 T
(int[]
)类型项的数组,它应该给出如下内容:
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]
其中 T
是 int[]
:我想要一个外部维度为 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
的范围可以从0
到NUM_ROWS-1
,所以它与初始化var myArray = new int[NUM_ROWS][]
的访问语法是对称的。这样,左边一组括号仍然对应二维数组的长度,右边一组括号仍然对应一维数组
编辑: 我看到有人在评论中发帖 a link to an article where Eric Lippert goes through a more elaborate example and justification。