为什么C#8阵列切片功能的新hat-operator索引从0开始?

Mic*_*ino 149 c# arrays indexing c#-8.0

C#8.0引入了一种切片阵列的便捷方法 - 参见官方C#8.0博文.

访问数组的最后一个元素的语法是

int value[] = { 10, 11, 12, 13 };

int a = value[^1]; // 13
int b = value[^2]; // 12
Run Code Online (Sandbox Code Playgroud)

我想知道为什么向后访问元素的索引从1开始而不是0?这有技术原因吗?

Mar*_*und 165

官方答复

为了更好的可见性,Mads Torgersen发表了一篇评论,解释了C#8博客文章中的这一设计决定:

当涉及到从头开始和从头算术时,我们决定遵循Python. 0 指定第一个元素(一如既往)和  ^0 "长度"元素,即一个直接结束的元素.这样你就可以得到一个简单的关系,其中元素从开始的位置加上它从末尾的位置等于长度.在  x 中  ^x 是什么,你会从长度减去如果你自己做的数学.

为什么不使用minus(-)而不是new hat(^)运算符?这主要与范围有关.再次与Python和大多数行业保持一致,我们希望我们的范围在开始时是包容性的,最后是独占的.你传递的索引是什么,说一个范围应该一直到最后?在C#中,答案很简单:x..^0从最后  x 到最后.在Python中,没有明确的索引可以给出:-0不起作用,因为它等于0第一个元素!所以在Python中,你必须完全抛弃结束索引来表达一个结束的范围:x...如果计算了范围的结尾,那么你需要记住具有特殊逻辑以防万一0.如在x..-y,y计算和出来的地方0.这是一个常见的麻烦和错误来源.

最后,请注意索引和范围是.NET/C#中的第一类类型.它们的行为与它们应用的内容无关,甚至不能用于索引器.您可以完全定义自己的索引器,它接受索引和另一个索引Range- 我们将添加这样的索引器,例如  Span.但是,您也可以使用采用范围的方法.

我的答案

我认为这是为了匹配我们习惯的经典语法:

value[^1] == value[value.Length - 1]
Run Code Online (Sandbox Code Playgroud)

如果它使用0,那么当并排使用这两种语法时会很混乱.这样它具有较低的认知负荷.

像Python这样的其他语言也使用相同的约定.

  • 对Mads评论进行小修正:你不能**在python中完全不使用末尾索引.你可以用'None`代替数字:`[0,1,2,3,4] [2:None] == [2,3,4]`.但是,是的,你不能使用整数作为结束索引(显然没有计算长度). (13认同)
  • @mowwwalker - 报价中是不是已经涵盖了?"所以在Python中......如果计算了范围的结尾,那么你需要记住在出现0时才有特殊的逻辑" (8认同)
  • 等等......`x..`出了什么问题?这似乎很好,我从来没有遇到python` [3:]`语法的问题. (4认同)
  • 很高兴看到他们没有用-0的东西重复Python的错误.处理这种特殊情况是一个巨大的麻烦,太容易忘记了. (4认同)
  • @mowwwalker Mads评论是关于你不知道索引值是什么的情况,因为它是以某种方式计算的.他们说,如果你想将`endIndex`计算为负指数(即从末尾开始的索引),你就会在负数和正数之间产生不连续性,因为"0"不会以正确的方式工作.那种情况.正如我所指出的那样,你必须将'0`替换为'None`.这意味着您的代码应该看起来像`seq [startIndex:endIndex或None]`.如果`endIndex`预期为正,则应省略`或None`. (3认同)
  • @mowwwalker没什么不对.我似乎也支持`x..`语法.这是[范围提案]的例子(https://github.com/dotnet/csharplang/blob/master/proposals/ranges.md) (2认同)