索引器是编译器生成get_item/set_item方法的特殊属性,并非数组替代品或语法糖;易错三点:this前须声明返回类型、value仅set中有效、参数类型不能为void;多参数或非int索引需警惕性能与可维护性陷阱。

索引器不是数组的替代品,也不是语法糖,它是编译器生成 get_Item/set_Item 方法的特殊属性;写错会直接报 CS0103、CS0102 或运行时 IndexOutOfRangeException,不能靠“看起来像数组”就忽略边界和语义。
索引器基础写法里最容易写错的三处
新手常把索引器当成方法来写,结果编译失败。关键点只有三个:
-
this前必须声明返回类型(比如string),但不能写public string GetItem(int index)——这是普通方法,不是索引器 -
value只在set里有效;在get里写return value;会触发CS0103 - 参数类型不能是
void,但可以是string、Guid、甚至DateTime,只要逻辑自洽
多参数或非 int 索引器的实际代价
支持 obj["user", 123] 看起来很酷,但要注意它不是语法优化,而是性能和可维护性陷阱:
- 调用时参数顺序严格绑定:不能写成
obj[123, "user"],否则编译失败 - 如果内部用
FirstOrDefault查找,就是O(n);而Dictionary<string t></string>是O(1),别用索引器掩盖低效设计 - 无法被 LINQ 的
ElementAt或Where直接识别——索引器只是语法糖,不是集合契约
更推荐的做法:该用字典就用字典,索引器只用于封装已有集合的访问逻辑,而不是替代数据结构。
接口和继承中索引器的隐性约束
接口定义索引器时只写签名,不实现;但实现类必须严格匹配可访问性:
- 接口里没写
get,实现类就不能只写set;反之亦然 - 子类重写父类索引器时,若用
base[index],得自己做越界检查——父类的检查不会自动继承 - 如果类同时实现两个含相同签名的接口(如
IReadOnlyList<t></t>和自定义IApiResponse<t></t>),必须用显式接口实现,例如:string IReadOnlyList<string>.this[int index] { get => ... }</string>
另外注意:编译器默认为索引器生成名为 Item 的属性,如果你手动加了 public object Item { get; },会触发 CS0102;解决方法是加 [IndexerName("Data")] 改名。
为什么有时候索引器比方法更危险
索引器的调用表象太像数组,容易让人忽略它的实际开销和契约缺失:
- 没有内置边界检查——
get中不手动校验index,就会直接抛IndexOutOfRangeException - 不支持
ref或out参数传递,想原地修改值必须靠额外方法 - 泛型索引器若未约束类型参数(比如没加
where T : class),在值类型上可能引发装箱/拆箱意外开销
最易被忽略的一点:索引器的 get 访问器一旦被用于 LINQ 查询(如 Where(x => x[0] == 'a')),它不会被翻译成表达式树,而是触发即时执行——这在 EF Core 等 ORM 场景下极易导致 N+1 查询。










