为什么 C# 8 数组切片功能中的新帽子运算符索引不从 0 开始?
Why doesn't the new hat-operator index from the C# 8 array-slicing feature start at 0?
C# 8.0 引入了一种便捷的数组切片方法 - 请参阅 official C# 8.0 blogpost。
访问数组最后一个元素的语法是
var value = new[] { 10, 11, 12, 13 };
int a = value[^1]; // 13
int b = value[^2]; // 12
我想知道为什么向后访问元素的索引从 1 而不是 0 开始?这有技术原因吗?
官方回答
为了更好的可见性,这里是 Mads Torgersen 的评论,解释了来自 C# 8 blog post:
的设计决策
We decided to follow Python when it comes to the from-beginning and from-end arithmetic. 0
designates the first element (as always), and ^0
the “length’th” element, i.e. the one right off the end. That way you get a simple relationship, where an element's position from beginning plus its position from end equals the length. the x
in ^x
is what you would have subtracted from the length if you’d done the math yourself.
Why not use the minus (-
) instead of the new hat (^
) operator? This primarily has to do with ranges. Again in keeping with Python and most of the industry, we want our ranges to be inclusive at the beginning, exclusive at the end. What is the index you pass to say that a range should go all the way to the end? In C# the answer is simple: x..^0
goes from x
to the end. In Python, there is no explicit index you can give: -0
doesn’t work, because it is equal to 0
, the first element! So in Python, you have to leave the end index off completely to express a range that goes to the end: x..
. If the end of the range is computed, then you need to remember to have special logic in case it comes out to 0
. As in x..-y
, where y
was computed and came out to 0
. This is a common nuisance and source of bugs.
Finally, note that indices and ranges are first class types in .NET/C#. Their behavior is not tied to what they are applied to, or even to be used in an indexer. You can totally define your own indexer that takes Index and another one that takes Range
– and we’re going to add such indexers to e.g. Span
. But you can also have methods that take ranges, for instance.
我的回答
我认为这是为了匹配我们习惯的经典语法:
value[^1] == value[value.Length - 1]
如果它使用 0,当这两种语法并排使用时会造成混淆。这样它的认知负荷就 较低 。
其他语言如 Python 也使用相同的约定。
C# 8.0 引入了一种便捷的数组切片方法 - 请参阅 official C# 8.0 blogpost。
访问数组最后一个元素的语法是
var value = new[] { 10, 11, 12, 13 };
int a = value[^1]; // 13
int b = value[^2]; // 12
我想知道为什么向后访问元素的索引从 1 而不是 0 开始?这有技术原因吗?
官方回答
为了更好的可见性,这里是 Mads Torgersen 的评论,解释了来自 C# 8 blog post:
的设计决策We decided to follow Python when it comes to the from-beginning and from-end arithmetic.
0
designates the first element (as always), and^0
the “length’th” element, i.e. the one right off the end. That way you get a simple relationship, where an element's position from beginning plus its position from end equals the length. thex
in^x
is what you would have subtracted from the length if you’d done the math yourself.Why not use the minus (
-
) instead of the new hat (^
) operator? This primarily has to do with ranges. Again in keeping with Python and most of the industry, we want our ranges to be inclusive at the beginning, exclusive at the end. What is the index you pass to say that a range should go all the way to the end? In C# the answer is simple:x..^0
goes fromx
to the end. In Python, there is no explicit index you can give:-0
doesn’t work, because it is equal to0
, the first element! So in Python, you have to leave the end index off completely to express a range that goes to the end:x..
. If the end of the range is computed, then you need to remember to have special logic in case it comes out to0
. As inx..-y
, wherey
was computed and came out to0
. This is a common nuisance and source of bugs.Finally, note that indices and ranges are first class types in .NET/C#. Their behavior is not tied to what they are applied to, or even to be used in an indexer. You can totally define your own indexer that takes Index and another one that takes
Range
– and we’re going to add such indexers to e.g.Span
. But you can also have methods that take ranges, for instance.
我的回答
我认为这是为了匹配我们习惯的经典语法:
value[^1] == value[value.Length - 1]
如果它使用 0,当这两种语法并排使用时会造成混淆。这样它的认知负荷就 较低 。
其他语言如 Python 也使用相同的约定。