支持范围的自定义类型
为自定义类型添加 Index 和 Range 支持。
支持范围的自定义类型 是 CoddyKit 上的免费 C# Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C# Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C# Academy 课程共包含 4 节课。
支持 Index 和 Range 的两种方式
您自己的类型也可以支持 Index 和 Range 语法。有两种实现方式:提供显式索引器,或提供 Length/Count 属性以及 Slice 方法。
显式 Index 索引器
添加一个接收 System.Index 的索引器后,^ 运算符就可以直接用于您的类型。
using System;
class Deck {
private int[] cards = { 1, 2, 3, 4, 5 };
public int this[Index i] => cards[i];
}
var d = new Deck();
Console.WriteLine(d[^1]); // 5显式 Range 索引器
要支持使用 .. 进行切分,请添加一个接收 System.Range 并返回所需 slice 类型的索引器。
using System;
class Deck {
private int[] cards = { 1, 2, 3, 4, 5 };
public int[] this[Range r] => cards[r];
}
var d = new Deck();
Console.WriteLine(string.Join(",", d[1..3])); // 2,3Length + Slice 模式
如果您的类型具有 Count 或 Length 属性以及 Slice(int start, int length) 方法,编译器也可以自动生成范围支持。
实现这一模式
这里的自定义缓冲区提供了 Length 和 Slice。编译器会为您将 buf[1..3] 重写为 buf.Slice(1, 2)。
using System;
class Buffer {
private int[] data = { 10, 20, 30, 40 };
public int Length => data.Length;
public int this[int i] => data[i];
public int[] Slice(int start, int length) {
var result = new int[length];
Array.Copy(data, start, result, 0, length);
return result;
}
}
var buf = new Buffer();
Console.WriteLine(string.Join(",", buf[1..3])); // 20,30Length 为什么重要
Length(或 Count)属性让编译器能够在您的类型上解析 ^1 这样的从末尾索引。没有它,^ 就无法转换。
using System;
class Buffer {
private int[] data = { 10, 20, 30, 40 };
public int Length => data.Length;
public int this[Index i] => data[i.GetOffset(Length)];
}
var buf = new Buffer();
Console.WriteLine(buf[^1]); // 40手动解析 Index
在 Index 索引器内部,调用 i.GetOffset(Length),将可能从末尾开始的索引转换为普通偏移量。
using System;
class Ring {
private string[] items = { "a", "b", "c" };
public int Length => items.Length;
public string this[Index i] {
get {
int offset = i.GetOffset(Length);
return items[offset];
}
}
}
var r = new Ring();
Console.WriteLine(r[^2]); // b手动解析 Range
对于 Range 索引器,r.GetOffsetAndLength(Length) 会同时提供要复制的起始位置和数量。
using System;
class Ring {
private int[] items = { 1, 2, 3, 4, 5 };
public int Length => items.Length;
public int[] this[Range r] {
get {
var (start, len) = r.GetOffsetAndLength(Length);
var slice = new int[len];
Array.Copy(items, start, slice, 0, len);
return slice;
}
}
}
var r = new Ring();
Console.WriteLine(string.Join(",", r[^3..^1])); // 3,4Count 与 Length
这一模式接受任意一个名称:数组和跨度使用 Length;List<T> 使用 Count。任一属性都能满足编译器的要求。
using System;
using System.Collections.Generic;
var list = new List<int> { 1, 2, 3, 4 };
// List<T> has Count, so Index works:
Console.WriteLine(list[^1]); // 4设计 Slice 的返回类型
请决定您的 Slice 返回副本(如数组)还是视图(如跨度)。视图的开销更低,但会共享可变状态,因此请记录这一选择。
完整的自定义类型
这个 Sequence 通过显式索引器和 Length 属性同时支持 ^ 和 ..。
using System;
class Sequence {
private int[] data;
public Sequence(int[] d) => data = d;
public int Length => data.Length;
public int this[Index i] => data[i];
public int[] this[Range r] => data[r];
}
var s = new Sequence(new[] { 5, 10, 15, 20 });
Console.WriteLine(s[^1]); // 20
Console.WriteLine(string.Join(",", s[1..^1])); // 10,15快速检查
确认自定义类型支持范围语法的条件。
回顾
您已经学会了向自己的类型添加 Index/Range 支持的两种方式。
- 提供接收
Index和Range的显式索引器。 - 使用隐式模式:提供
Count/Length属性和Slice(int, int)。 GetOffset(Length)和GetOffsetAndLength(Length)可以解析从末尾开始的位置。
常见问题解答
「支持范围的自定义类型」课时是免费的吗?
是的 — 「支持范围的自定义类型」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 4 节课。
「支持范围的自定义类型」这节课中我会学到什么?
为自定义类型添加 Index 和 Range 支持。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C# Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「支持范围的自定义类型」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C# Academy 课中编写并运行代码吗?
能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。