C# 切片语法糖(范围和索引)

语法

索引

^ 末尾运算符

代表尾部索引,例:

  • nums[^1]:取最后 1 个元素
  • nums[^1]:取倒数第 2 个元素

范围

.. 范围运算符

代表左闭右开范围,例:

  • nums[1..5]:取数组索引 [1,5) 这 4 个元素,也可以理解为 nums.Skip(1).Take(4)
范围中允许使用尾部索引

例:

  • nums[1..^1]:取数组索引从 1 开始的元素到最后的前 1 个元素
  • nums[^5..^2]:取数组从倒数第 5 个元素(包含)到倒数第 2 个元素(不包含)
范围中允许省略索引

nums[x..y] 允许省略 x 和/或 y,例:

  • nums[1..]:取数组索引从 1 开始的元素到最后
  • nums[..5]:取数组前 5 个元素
  • nums[^5..]:取数组从倒数第 5 个元素到最后
  • nums[..]:取数组所有元素

官方示例

int[] oneThroughTen =
[
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10
];

Write(oneThroughTen, ..);
Write(oneThroughTen, ..3);
Write(oneThroughTen, 2..);
Write(oneThroughTen, 3..5);
Write(oneThroughTen, ^2..);
Write(oneThroughTen, ..^3);
Write(oneThroughTen, 3..^4);
Write(oneThroughTen, ^4..^2);

static void Write(int[] values, Range range) =>
    Console.WriteLine($"{range}:\t{string.Join(", ", values[range])}");
// Sample output:
//      0..^0:      1, 2, 3, 4, 5, 6, 7, 8, 9, 10
//      0..3:       1, 2, 3
//      2..^0:      3, 4, 5, 6, 7, 8, 9, 10
//      3..5:       4, 5
//      ^2..^0:     9, 10
//      0..^3:      1, 2, 3, 4, 5, 6, 7
//      3..^4:      4, 5, 6
//      ^4..^2:     7, 8

类型支持

支持索引(Index)的类型

  • 必须有 LengthCount 属性(返回 int

  • 必须有 索引器 this[int]

支持范围(Range)的类型

  • 必须有 LengthCount 属性(返回 int

  • 必须有 Slice(int start, int length) 方法

标准类型支持

数组、StringSpan<T> 等同时支持索引和范围。

List<T> 支持索引,但不支持范围。

自定义类型支持

var myString = new MyString("Hello");

Console.WriteLine(myString[^1]);   // o
Console.WriteLine(myString[1..4]); // ell


class MyString(string s)
{
    // Length 或 Count
    public int Length => s.Length;

    // 支持索引
    public char this[int index] => s[index];

    // 支持范围
    public MyString Slice(int start, int length)
        => new MyString(s.Substring(start, length));

    public override string ToString() => s;
}

说明

  • 从数组中获取范围时,结果是原数组的副本。修改新数组时,不会更改原数组的值。
  • 使用范围运算符的性能取决于序列类型。例如如果序列是数组或 string,结果是原始部分的副本,时间复杂度为 O(N);如果序列是 System.Span<T>,结果仍然为指针引用,没有副本,时间复杂度为 O(1)
posted @ 2025-08-18 11:11  Varc  阅读(45)  评论(0)    收藏  举报