索引器本质是编译器生成的特殊方法,必须命名为get_Item和set_Item,带参数且标记SpecialName属性;不支持重载返回类型、ref/out参数、set中return语句或泛型协变。

索引器本质是编译器生成的特殊方法
C# 中的 Indexer 不是语法糖,而是编译器强制约定的一组命名方法:必须叫 get_Item 和 set_Item,且必须带参数(至少一个)。IL 反编译后你看到的就是这两个普通实例方法,只是元数据标记了 SpecialName 属性,让 C# 编译器允许用方括号调用。
这意味着:
- 不能重载 get_Item 的返回类型(只能有一个)
- 多个索引器参数会变成多参数的 get_Item(int, string),不是重载多个 get_Item
- 如果类同时有 this[int] 和 this[string],编译器会报错 —— 因为它们都会试图生成同名的 get_Item
声明索引器时参数类型和数量决定调用方式
索引器参数不限于 int,但必须是可比较、可哈希(如果用于字典式场景)或业务语义明确的类型。常见误用是盲目套用数组习惯:
- 用
string当索引 → 实际是模拟字典访问,应优先考虑继承IDictionary<K,V>或封装Dictionary<K,V>,而非裸写this[string] - 用多个参数(如
this[int row, int col])→ 编译后是get_Item(int, int),但无法像二维数组那样支持arr[1][2]链式调用,只能写成arr[1, 2] - 参数含
ref或out→ 编译失败,索引器的 get/set 签名不支持修饰符
set 访问器里 return 语句会导致编译错误
set 访问器隐式接收一个名为 value 的参数,类型与索引器声明的返回类型一致。它没有返回值(即 void),所以以下写法会报错:
set { if (value != null) { _items[_index] = value; return; } }原因:return 在 set 中非法,编译器不允许显式退出。正确做法是直接赋值或抛异常:
- 校验失败应 throw
ArgumentException,而不是静默 return - 若需延迟赋值(如触发事件),直接操作内部字段或调用私有方法即可
-
value是只读形参,不可重新赋值(value = ...报错)
泛型类中索引器要小心协变/逆变影响
在泛型类 MyList<T> 中定义 this[int i] 很自然,但如果 T 是接口或基类,且你希望支持子类型安全读取,要注意:索引器本身不参与泛型协变(out T)声明。也就是说,即使你写了 class MyList<out T>,也不能给 this[int] 的 get 声明返回 T —— 因为索引器不是成员方法,不享受泛型变型规则。
更实际的问题是:当你把 MyList<string> 赋给 IReadOnlyList<object> 变量时,索引访问仍走原类型的 get_Item,不会自动转型。真正需要类型安全下标访问的场景,往往该用 IReadOnlyList<T> 接口,而不是自己重复实现索引器。
自定义索引器真正的价值不在“看起来像数组”,而在于封装非线性访问逻辑 —— 比如按时间戳查日志、按哈希前缀分片查缓存。这些地方一旦把边界条件、线程安全、空值处理漏掉,比语法错误更难调试。


















