Index 和 Range 是 C# 8.0 引入的独立值类型结构体,非 int 的语法糖;^n 合法范围为 ^1 至 ^length,^0 编译报错;Range 切片返回新数组,有堆分配开销。
Index 和 Range 是什么类型,为什么不能直接当 int 用
Index
和
Range
是 C# 8.0 引入的独立结构体类型,不是语法糖的“假类型”,而是有实际行为的值类型。它们不隐式转换为
int
,所以写
arr[index]
时,
index
必须是
Index
类型(比如
^3
),不能是
int
变量再强转——编译器会报错
Cannot convert from 'int' to 'System.Index'
。
常见错误:把
var i = ^3;
误以为是
int
,然后传给自定义方法 expecting
int
参数,结果编译失败。
实操建议:
要用
Index
就显式声明:
Index i = ^3;
或直接用字面量
^n
需要转成实际下标时,调用
i.GetValueFromLength(len)
,例如
^3.GetValueFromLength(10) == 7
Range
同理,
range.Start.GetValueFromLength(len)
和
range.End.GetValueFromLength(len)
才是真实索引
Range 切片后得到的是新数组还是视图
C# 的
arr[1..4]
返回的是一个
新分配的数组
,不是 Span
或只读视图。这意味着每次切片都触发堆分配,对大数组或高频调用场景有明显 GC 压力。
使用场景判断很关键:
临时取几项做计算、日志、简单转换 → 用
Range
没问题,代码干净
要避免复制(如解析二进制流、高性能图像处理)→ 改用
Span
或
Memory
,配合
array.AsSpan()[1..4]
想复用已有缓冲区又需切片逻辑 → 直接操作
Span
,别碰
Range
语法
注意:
string
的
[Range]
也是返回新字符串,同样涉及内存分配。
^ 运算符的边界行为和常见越界陷阱
^n
表示“从末尾往前数第 n 个”,但它的合法范围是
^1
到
^length
(含),
^0
是非法的,编译直接报错
Index must be positive
。
C知道
CSDN推出的一款AI技术问答工具
下载
容易踩的坑:
动态计算
^k
时没校验
k > 0 && k ,运行时报 Index was outside the bounds of the array
误以为
^0
等价于
array.Length
—— 实际上
^1
才是最后一个元素,
^array.Length
才是第一个
在
Range
中混用正负索引:
arr[^5..^1]
合法,但
arr[^5..3]
会被解释为
arr[^5..^array.Length + 3]
,语义易混淆,建议统一用
^
或全用正数
和 LINQ Take/Skip 对比:什么时候该用 Range
Range
是语言级切片,编译后直接转为
Array.Copy
或类似高效拷贝;而
Take(3).Skip(2)
返回
IEnumerable
,延迟执行、可能多次枚举、且无长度预知。
选
Range
更合适的情况:
明确知道源是数组或
string
,且需要一份独立副本
切片逻辑固定(如取后三位:
str[^3..]
),而非依赖运行时条件拼接
性能敏感,且切片范围小、频次高 ——
Range
比等效 LINQ 快 3–5 倍(实测,.NET 6+)
别硬套
Range
的情况:
源是
IEnumerable
(比如数据库查询结果、文件流行迭代器)→ 它不支持
Range
,编译不过
切片逻辑复杂,带过滤或状态判断 → 还是老实用 LINQ 或 for 循环更清晰
真正容易被忽略的一点:Range 不支持多维数组,
matrix[1..3, 2..4]
是非法的,必须降维或手动循环处理。