日期:2010-06-13  浏览次数:20584 次

索引器

索引器(Indexer)是C#引入的一个新型的类成员,它使得对象可以像数组那样被方便,直观的引用。索引器非常类似于我们前面讲到的属性,但索引器可以有参数列表,且只能作用在实例对象上,而不能在类上直接作用。下面是典型的索引器的设计,我们在这里忽略了具体的实现。

class MyClass{    public object this [int index]    {        get        {            // 取数据        }        set         {            // 存数据        }    }}
索引器没有像属性和方法那样的名字,关键字this清楚地表达了索引器引用对象的特征。和属性一样,value关键字在set后的语句块里有参数传递意义。实际上从编译后的IL中间语言代码来看,上面这个索引器被实现为:

class MyClass{    public object get_Item(int index)    {          // 取数据    }    public void set_Item(int index, object value) {//存数据    }}
由于我们的索引器在背后被编译成get_Item(int index)和set_Item(int index, object value)两个方法,我们甚至不能再在声明实现索引器的类里面声明实现这两个方法,编译器会对这样的行为报错。这样隐含实现的方法同样可以被我们进行调用,继承等操作,和我们自己实现的方法别无二致。通晓C#语言底层的编译实现为我们下面理解C#索引器的行为提供了一个很好的基础。

和方法一样,索引器有5种存取保护级别,和4种继承行为修饰,以及外部索引器。这些行为同方法没有任何差别,这里不再赘述。唯一不同的是索引器不能为静态(static),这在对象引用的语义下很容易理解。值得注意的是在覆盖(override)实现索引器时,应该用base[E]来存取父类的索引器。

和属性的实现一样,索引器的数据类型同时为get语句块的返回类型和set语句块中value关键字的类型。

索引器的参数列表也是值得注意的地方。“索引”的特征使得索引器必须具备至少一个参数,该参数位于this关键字之后的中括号内。索引器的参数也只能是传值类型,不可以有ref(引用)和out(输出)修饰。参数的数据类型可以是C#中的任何数据类型。C#根据不同的参数签名来进行索引器的多态辨析。中括号内的所有参数在get和set下都可以引用,而value关键字只能在set下作为传递参数。

下面是一个索引器的具体的应用例子,它对我们理解索引器的设计和应用很有帮助。

using System;class BitArray{int[] bits;int length;public BitArray(int length) {if (length < 0) throw new ArgumentException();bits = new int[((length - 1) >> 5) + 1];this.length = length;}public int Length {get { return length; }}public bool this[int index] {get {if (index < 0 || index >= length) throw new IndexOutOfRangeException();elsereturn (bits[index >> 5] & 1 << index) != 0;}set{if (index < 0 || index >= length)throw new IndexOutOfRangeException();else if(value) bits[index >> 5] |= 1 << index;elsebits[index >> 5] &= ~(1 << index);}}}class Test{static void Main() {BitArray Bits=new BitArray(10);for(int i=0;i<10;i++)Bits[i]=(i%2)==0;                        Console.Write(Bits[i]+"  ");}}
编译并运行程序可以得到下面的输出:

True False True False True False True False True False
上面的程序通过索引器的使用为用户提供了一个界面友好的bool数组,同时又大大降低了程序的存储空间代价。索引器通常用于对象容器中为其内的对象提供友好的存取界面--这也是为什么C#将方法包装成索引器的原因所在。实际上,我们可以看到索引器在.NET Framework类库中有大量的应用。

操作符重载

操作符是C#中用于定义类的实例对象间表达式操作的一种成员。和索引器类似,操作符仍然是对方法实现的一种逻辑界面抽象,也就是说在编译成的IL中间语言代码中,操作符仍然是以方法的形式调用的。在类内定义操作符成员又叫操作符重载。C#中的重载操作符共有三种:一元操作符,二元操作符和转换操作符。并不是所有的操作符都可以重载,三种操作符都有相应的可重载操作符集,列于下表:

一元操作符+ - ! ~ ++ -- true false
二元操作符+ - * / % & | ^ << >> == != > < >= <=
转换操作符隐式转换()和显式转换()
重载操作符必须是public和static 修饰的,否则会引起编译错误,这在操作符的逻辑语义下是不言而喻的。父类的重载操作符会被子类继承,但这种继承没有覆盖,隐藏,抽象等行为,不能对重载操作符进行virtual sealed override abstract修饰。操作符的参数必须为传值参数。我们下面来看一个具体的例子:

using System;class Complex{double  r, v;  //r+ v ipublic Complex(double r, double v){this.r=r;this.v=v;}public static Complex operator +(Complex a, Complex b) {return new Complex(a.r+b.r, a.v+b.v);}public static Complex operator -(Complex a){return new Complex(-a.r,-a.v);}public static Complex operator ++(Complex a) {  double r=a.r+1;  double v=a.v+1;return new Complex(r, v);}public void Print(){Console.Write(r+" + "+v+"i");}}class Test{public static void Main(){Complex a=new Complex(3,4);Complex b=new Complex(5,6);Complex c=-a;c.Print();Complex d=a+b;d.Print();a.Print();Complex e=a++;a.Print();e.Print();Compl