C#语言系列讲座(8) 索引器与操作符重载

转载 2004年09月01日 12:08:00
索引器

索引器(Indexer)是C#引入的一个新的类成员,它使得对象可以像数组那样被方便、直观地引用。索引器非常类似于前面讲到的属性,不同的是索引器可以有参数列表,但只能作用在实例对象上,而不能直接作用在类上。

索引器没有像属性和方法那样的名字,关键字this清楚地表达了索引器引用对象的特征。和属性一样,value关键字在set后的语句块里有参数传递的意义。实际上从编译后的IL中间语言代码来看,上面这个索引器里的set 和get函数被实现为get_Item(int index)和set_Item(int index, object value)两个方法,所以不能再在实现索引器的类里声明实现这两个方法,编译器会对这样的行为报错。这样隐含实现的方法同样可以进行调用、继承等操作,与代码中实现的方法相同。

和方法一样,索引器有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 IndexOutOfRange-

Exception();

else

return (bits[index >> 5] & 1 << index) != 0;

}

set {

if (index < 0 || index >= length)

throw new IndexOutOfRange-

Exception();

else if(value)

bits[index >> 5] |= 1 << index;

else

bits[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;

for(int i=0;i<Bits.Length;i++)

Console.Write(Bits[i]+“ ”);

}

}

编译并运行程序,可以得到下面的输出:

True False True False True False True False True False

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

操作符重载

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

重载操作符必须是public和static 修饰的,否则会引起编译错误。父类的重载操作符会被子类继承,但这种继承没有覆盖、隐藏、抽象等行为,不能对重载操作符进行virtual、sealed、override、abstract修饰。操作符的参数必须为传值参数。下面是一个具体的示例:

using System;

class Complex{

double r, v; //r+ v i

public 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();

Complex f=++a;

a.Print();

f.Print();

}

}

编译程序并运行,可得到下面的输出:

-3 + -4i 8 + 10i 3 + 4i 4 + 5i 3 + 4i 5 + 6i 5 + 6i

这里实现了一个“+”号二元操作符、一个“-”号一元操作符(取负值)和一个“++”一元操作符。这里没有对传进来的参数做任何改变——这在参数是引用类型变量时尤其重要,注意重载操作符的参数只能是传值方式。在返回值时,往往需要“new”一个新的变量(除了true和false操作符),这在重载“++”和“--” 操作符时常用,也就是说在做“a++”时,将丢弃原来的a值,而取代的是新的“new”出来的值!

操作符重载对返回值和参数类型有着相当严格的要求:一元操作符中只有一个参数;操作符“++”和“--”返回值类型和参数类型必须和声明该操作符的类型一样;操作符“+ - ! ~”的参数类型必须和声明该操作符的类型一样,但返回值类型可以任意;true和false操作符的参数类型必须和声明该操作符的类型一样,而返回值类型必须为bool,而且必须配对出现——也就是说只声明其中一个是不对的,会引起编译错误。参数类型的不同会导致同名操作符的重载——实际上这是方法重载的表现。

二元操作符参数必须为两个,而且必须至少有一个的参数类型为声明该操作符的类型,返回值类型可以任意。有三对操作符必须配对声明出现,它们是“==”、“!=”、“>”与“<”、“>=”与“<=”。需要注意的是两个参数的类型不同,或虽然类型相同但顺序不同都会导致同名操作符的重载。

转换操作符为不同类型之间提供隐式转换和显式转换,主要用于方法调用、转型表达和赋值操作。转换操作符对其参数类型(被转换类型)和返回值类型(转换类型)也有严格的要求。参数类型和返回值类型不能相同,且两者之间必须至少有一个和定义操作符的类型相同。转换操作符必须定义在被转换类型或转换类型任何其中一个里面。不能对系统定义过的转换操作进行重新定义,两个类型也都不能是object或接口类型,两者之间不能有直接或间接的继承关系——这三种情况系统已经默认转换。

实际上,对于属性、索引器和操作符这些C#提供的界面操作,都是方法的某种形式的逻辑抽象包装,它旨在提供一个友好易用的界面,完全可以通过方法来实现它们实现的功能。理解了这样的设计初衷,才会恰当、正确地用好这些操作,而不至于导致滥用和错用。

C#学习日记28---索引器、重载索引器

索引器允许类或者结构的实例就像数组一样进行索引,当您为一个类定义一个索引器时,这个类就会像一个数组一样,您可以使用数组访问符 '[]' 对该类的实例进行访问.. 索引器与 属性  类似,但是索引器要...
  • HC666
  • HC666
  • 2015年10月20日 22:39
  • 2427

C#语法小知识(二十三)重载运算符

C#里使用Operator关键字定义静态方法来重载运算符。
  • ecidevilin
  • ecidevilin
  • 2016年11月29日 22:49
  • 785

探索Scala(1)-- 运算符重载

Scala语言运算符重载完全是语法层面的小把戏,本文记录我对Scala语言运算符重载的一些理解...
  • SpiderDog
  • SpiderDog
  • 2014年10月14日 14:39
  • 3316

C语言之重载运算符()/[]/++/->等运算符

C语言之重载运算符()/[]/++/->等预算符文章链接:知识点 重载()运算符; 重载[]运算符; 重载++和->运算符; 新名词记录{operator} 概述operator操作符一文包括两种用法...
  • qq_16628781
  • qq_16628781
  • 2017年05月22日 20:23
  • 1506

C#学习笔记--this关键字和索引器

this关键字和索引器
  • chensi16114
  • chensi16114
  • 2016年07月13日 09:34
  • 3774

重载一元、二元运算符

8.6 重载一元运算符 类的一元运算符可重载为一个没有参数的非static成员函数或者带有一个参数的非成员函数,参数必须是用户自定义类型的对象或者对该对象的引用。实现重载运算符的成员...
  • lycnjupt
  • lycnjupt
  • 2015年07月25日 20:50
  • 1165

C++ STL 基础及应用(2) 模板与操作符重载

模板是 C++ 语言中重要的概念。它提供了一种通用的方法来开发重用的代码,即以创建参数化的 C++ 类型。 本章将阐述一些具体的 STL 模板思想,并简单介绍操作符重载与模板的联系。 文中将给出一个简...
  • Raito__
  • Raito__
  • 2016年05月22日 17:13
  • 2551

C++运算符重载(11) - 重载<<和>>操作符

运算符 “>”用于流输出。 在开始重载这些操作符之前,必须注意下面的事项: 1) cout是输出类的对象,而cin是输入类的对象 2) 这些操作符必须重载为全局函数。如果想要让它们可以访问私有成员,...
  • shltsh
  • shltsh
  • 2015年05月27日 00:11
  • 762

C#学习日记29----二维索引器 与 foreach遍历索引器

上一篇结尾的时候我留下了几个问题,因为要断电了没有解决,这一篇我们继续上一篇的内容。点这里回到上一篇 问题1:         数组有多维度的,索引器也可以是多维的吗???          ...
  • HC666
  • HC666
  • 2015年10月21日 19:37
  • 1995

C++运算符重载(8) - 默认赋值操作符和引用

前面的一篇文章已经讨论过了赋值操作符重载的问题,点此链接"C++运算符重载(3) - 什么时候需要自定义赋值操作符"。此篇文章是前一篇的扩展。在前面的这篇文章中,已经说过了什么当没有用户定义的赋值操作...
  • shltsh
  • shltsh
  • 2015年05月26日 09:57
  • 910
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C#语言系列讲座(8) 索引器与操作符重载
举报原因:
原因补充:

(最多只允许输入30个字)