C#中索引器的作用和好处是什么?

13

阅读一些代码时发现这段我之前没见过的片段:

public SomeClass {
  public someInterface this[String strParameter] {
    get {
      return SomeInternalMethod(strParameter);
    }
  }
}

看起来它是这样调用的:

SomeClass _someClass = new SomeClass();
SomeInterface returnedValue = _someClass["someString"];

我对于这个函数在哪些场景下比较合适或者说作者采用这种写法的目的很感兴趣。例如,为什么会优先选择这种写法而不是简单地调用函数呢?


1
它自然、优雅、令人愉悦,增强了理解。方法调用很无聊。看看这个:http://steve-yegge.blogspot.com/2006/03/execution-in-kingdom-of-nouns.html - Hans Passant
11个回答

23

参见语言规范第10.9节,其声明如下:

索引器是一种成员,它使对象可以像数组一样被索引。

索引器和属性在概念上非常相似,但在以下方面略有不同:

  • 属性通过名称进行标识,而索引器通过签名进行标识。
  • 属性可以通过简单名称(§7.5.2)或成员访问(§7.5.4)来访问,而索引器元素可以通过元素访问(§7.5.6.2)来访问。
  • 属性可以是静态成员,而索引器始终是实例成员。
  • 属性的get访问器对应于没有参数的方法,而索引器的get访问器对应于与索引器具有相同形式参数列表的方法。
  • 属性的set访问器对应于带有名为value的单个参数的方法,而索引器的set访问器对应于具有与索引器相同形式参数列表的方法,以及一个额外的名为value的参数。
  • 索引器访问器声明一个与索引器参数同名的局部变量将导致编译时错误。
  • 在覆盖属性声明中,继承的属性使用base.P语法进行访问,其中P是属性名称。在覆盖索引器声明中,继承的索引器使用base[E]语法进行访问,其中E是逗号分隔的表达式列表。

2
我知道我不应该抢占这个话题,但也许你可以写一篇关于为什么没有静态索引器的博客?当我看到它们不存在时,我感到震惊,因为它是一种如此简洁的方式。(在 Jon 提出好的 Encoding["UTF8"] 示例之前 :)) - Michael Stum
3
我刚刚查看了设计笔记存档,里面没有任何内容可以证明不使用静态索引器的决定是有道理的。因此,我只能依据我们通常不推出某个功能的解释:没有人认为这个想法足够好,值得花费金钱将其实现。我想这个解释也可以解释为什么索引器不能是通用的。 - Eric Lippert
很酷 :) “默认”解释的好处在于它并没有完全关闭这个可能性的大门。我不知道这个功能有多“重”(我不会因为不知道编译器和CLR实际工作方式而说“但这只是一个小改变!”而使自己失去资格),但这比“我们没有做这个因为X、Y和Z使得它永远无法实现”要好。 - Michael Stum
很棒的信息 - 我不知道它被称为索引器,所以我不知道从哪里开始寻找它的定义。我猜我被绊倒了,因为作者使用了一个带有字符串参数的只读索引器。如果是 int 类型,我会理解,因为你可以在循环等中使用它。使用字符串参数会给出一个字典类型的结构,但它是只读的,所以它真的感觉像一个方法,因为你不能传递一个值给它。 - Michael Gattuso

21

看起来很多答案都关注索引器的是什么,而不是为什么要使用它。

就我所知,以下是使用索引器的动机:

你正在处理一个有某种类型集合的类,但你希望这个类对用户(类的消费者)看起来像是一个集合。

我能想到的最好的例子是 ADO.NET 中的 DataRow 类。如果你想获取一个 DataRow 的第五个单元格的值,你可以使用 DataRow.Item[4]DataRow[4]。后一种形式是一个方便和逻辑上的快捷方式,并且它展示了为什么你想使用一个索引器。对于用户来说,DataRow 可以被认为是只是一个单元格的集合(尽管它实际上更多),因此直接获取和设置单元格值是有意义的,而不必记住你实际上是在获取/设置一个 Item

希望这有所帮助。


6
在很多情况下,“index”语法非常有意义。如果SomeClass代表某种集合,它特别有用。

3

它允许你进行关联数组查找(也称为“字典样式”),就像你在问题中提到的那样。

这正是重点所在。有些人喜欢这种方式,尤其是从已经内置了这种功能的语言(如Python或PHP)过来的人。


3

你是说:“this”关键字是一个索引器? - spender

1

你可能之前已经偶然发现过类似的东西:

var myList = new List<string>();
myList.Add("One");
myList.Add("Two");
myList.Add("Three");

for(int i = 0; i < myList.Count; i++) {
    string s = myList[i];
}

索引器是实现集合的对象的“主键”。它只是一种编写函数 .GetValue(index) 的简写方式 - 如果您愿意,可以称之为语法糖。但它也使意图清晰明了。

0

在我看来,这只是一种语法上的便利。 你不应该在哪些情况下使用它:

public SomeClass {
  private int[] nums;
  public GetVal this[int ind] {
    get {
      return nums[ind]; // this is pointless since array is already indexed
    }
  }
}

你会从中受益的地方:

public Matrix {
  private Dictionary<string, double> matrixData; // Matrix indecies, value
  public double this[int row, int col] {
    get {
      return matrixData[string.Format("{0},{1}", row, col)];
    }
  }
}

正如您所看到的,由于某种原因,您的数据是一个以字符串键索引的字典,并且您希望使用两个整数索引来调用它,但仍不想更改数据类型:

Matrix m = new Matrix();
...
Console.WriteLine( m[1,2] );

0
这是索引操作符[ ]的实现。

0
这是一个运算符重载。如果你正在编写一个集合类,例如使用数组表示法访问它会很有意义,比如 collection[someIndex]。
当然,你也可以编写一个 collection.GetElement(someIndex) 等效的函数,但这是一种风格和可读性的选择。

0

你可以在一个键值对类中使用这个方法。

我不确定在c#中有什么类似的类,但在c++ STL中有一个叫做map的类,你可以通过调用SomeObject["key"]方法来返回与该键相关联的“value”。


在C#中,类似的类是System.Collections.Generic.Dictionary(除了Dictionary使用哈希查找,而map则按键排序) - Andrew Shepherd

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接