如何在C#中重载方括号运算符?

274

例如,DataGridView允许您执行以下操作:

DataGridView dgv = ...;
DataGridViewCell cell = dgv[1,5];

但是就我而言,我找不到关于索引/方括号运算符的文档。他们称之为什么?它在哪里实现?它会抛出异常吗?如何在自己的类中进行相同的操作?

预计完成时间:感谢所有快速回答的人。简要地说:相关文档位于“Item”属性下;重载方法是声明一个属性,例如public object this[int x, int y] { get {...} ; set {...} };根据文档,DataGridView的索引器不会引发异常。它并未提到如果提供无效的坐标会发生什么。

再次更新:好吧,尽管文档没有提及(顽皮的微软!),但事实证明,如果您使用无效坐标提供它,DataGridView的索引器确实会引发ArgumentOutOfRangeException。请注意。

8个回答

412

你可以在这里找到如何进行操作。简单来说,就是:

public object this[int i]
{
    get { return InnerList[i]; }
    set { InnerList[i] = value; }
}

如果你只需要一个 getter,下面的回答中的语法也可以使用(从 C# 6 开始)。


9
一小点建议:根据你的具体情况,你可能会发现这样做更合适:获取 {return base[i];} 设置 {base[i] = value;}。 - MikeBaz - MSFT
9
这不是运算符重载,而是索引器。 - Destructor
6
索引器(Indexer)其实可以看作是一个一元操作符。 - alan2here
1
在2019年,应该选择一个新的答案,这个。很遗憾SO没有处理弃用答案的功能,因为新的答案还没有获得350多个赞,尽管它值得。 - mins
@mins 我在回答中附上了另一个答案的链接。 - Ruben

44

非常感谢!如果我可以设置两个答案,我也会添加你的答案——没有其他人知道要在文档中查找Item... - Coderer
5
实现时的称呼是“Item”,但在C#中被称为“this”。 - Marc Gravell
1
没错,但我问的是“我找不到关于索引/方括号运算符的文档”,我的意思是当你在MSDN中查找库类时,他们在哪里告诉你有关该运算符的信息?这就是为什么我最后提到了它会抛出什么异常 - 文档是错误的。 - Coderer

28
Operators                           Overloadability

+, -, *, /, %, &, |, <<, >>         All C# binary operators can be overloaded.

+, -, !,  ~, ++, --, true, false    All C# unary operators can be overloaded.

==, !=, <, >, <= , >=               All relational operators can be overloaded, 
                                    but only as pairs.

&&, ||                              They can't be overloaded

() (Conversion operator)            They can't be overloaded

+=, -=, *=, /=, %=                  These compound assignment operators can be 
                                    overloaded. But in C#, these operators are
                                    automatically overloaded when the respective
                                    binary operator is overloaded.

=, . , ?:, ->, new, is, as, sizeof  These operators can't be overloaded

    [ ]                             Can be overloaded but not always!

信息来源

对于方括号:

public Object this[int index]
{
    
}

##但是

数组索引运算符不能被重载;但是,类型可以定义索引器,即接受一个或多个参数的属性。索引器参数用方括号括起来,就像数组索引一样,但是索引器参数可以声明为任何类型(与数组索引不同,数组索引必须是整数)。

参考自MSDN


是的,它可以被重载,只要参数签名不同,就像任何其他方法的重载限制一样。 - Charles Bretana
它可以,但不是我写的那个条件。这是来自 MSDN 的。如果你不相信我,请查看源代码。 - Patrick Desjardins
2
抱歉如果我误读了你的帖子,但是你指的是哪个条件? - Charles Bretana
+1 给这个方便的列表。顺便说一下,链接已经失效了。(四年后,我知道了) - Mixxiphoid
我想补充一下,现在你可以在C#中覆盖隐式和显式类型转换。 - Felype

17
如果您正在使用C#6或更高版本,您可以使用表达式主体语法来获取只读索引器: public object this[int i] => this.InnerList[i];

5
public class CustomCollection : List<Object>
{
    public Object this[int index]
    {
        // ...
    }
}

6
实际上,这非常危险 - 现在你有两个竞争的实现:任何将变量类型定义为List<T>、IList<T>或IList等的人都无法执行你的自定义代码。 - Marc Gravell
1
同意 - 如果没有其他的,不需要从List派生CustomCollection,但我没有意识到它实际上是危险的。 - Coderer
它仍会执行自定义代码,不是吗?声明的变量类型无关紧要 - 关键是对象的类型。 - izb
如果您将对象传递给接受List<Object>的函数,则不会调用自定义代码。 - David Sykes
1
C# 多态的友情提醒:这是因为基类没有将其实现声明为虚拟的。如果它被声明为虚拟的,自定义代码将在每种情况下被调用。 - almulo
1
此外,这段代码会给你一个编译器警告,因为两个索引器都是非虚拟的。编译器会建议你使用 new 关键字来限定你的自定义索引器。 - amoss

4

这里是一个从内部List对象返回值的示例。应该可以给你一个想法。

  public object this[int index]
  {
     get { return ( List[index] ); }
     set { List[index] = value; }
  }

3

如果是使用/clr编译的CLI C++,请参阅此MSDN链接

简而言之,属性可以被命名为"default":

ref class Class
{
 public:
  property System::String^ default[int i]
  {
    System::String^ get(int i) { return "hello world"; }
  }
};

1

如果你指的是数组索引器,那么你只需要编写一个索引器属性就可以重载它。而且你可以重载(编写任意数量的)索引器属性,只要每个属性都有不同的参数签名。

public class EmployeeCollection: List<Employee>
{
    public Employee this[int employeeId]
    {   
        get 
        { 
            foreach(var emp in this)
            {
                if (emp.EmployeeId == employeeId)
                    return emp;
            }

            return null;
        }
    }

    public Employee this[string employeeName]
    {   
        get 
        { 
            foreach(var emp in this)
            {
                if (emp.Name == employeeName)
                    return emp;
            }

            return null;
        }
    }
}

2
首先,你应该用this[],而不是this()。但如果在一个列表(IList/IList<T>/List<T>)上提供了一个自定义的(但不同的)this[int],那么这是非常危险的,可能会在“int index”和“int employeeId”版本之间导致微妙的错误。两者仍然可以被调用。 - Marc Gravell
实际上,我认为如果不添加新的或覆盖语句,我输入的上面的代码无法编译,这正是由于List<T>实现了这个[int]的存在。你说得对,这样做的潜在风险是存在的,我只是把它作为重载索引器的示例。 - Charles Bretana

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