接下来本节将介绍另外一个重要的重载:运算符重载。学习到现在,像+和*这样的运算符只能用于预定义的数据类型,原因很简单:编译器认为所有常见的运算符都是用于这些数据类型的,例如,它知道如何把两个long加起来,或者如何从一个double中减去另一个double,并生成合适的中间语言代码。但在定义自己的类或结构时,必须告诉编译器:什么方法可以调用,每个实例存储了什么字段等所有的信息。同样,如果要在自己的类上使用运算符,就必须告诉编译器相关的运算符在这个类中的含义。此时就要定义运算符重载。
小天:能够用通俗点的语言描述下运算符的概念不?还有只有算术运算符可以重载吗?
老田:通俗的说,运算符重载就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型。其实将两个string类型相加这个运算符就重载过的。所以加号在面对数值类型的时候就是求和的作用。而面对两个string类型的时候就是连接字符串的作用。例如
int x = 5, y = 10; int z = x + 5;
string str1 = "老", str2 = "田"; string str3 = str1 + str2; |
而运算符重载不仅仅限于算术运算符。还需要考虑比较运算符 ==、<、>、!=、>=和<=。例如,语句if(a==b)。对于类,这个语句在默认状态下会比较引用a和b,检测这两个引用是否指向内存中的同一个地址,而不是检测两个实例是否包含相同的数据。对于string类,这种操作就会重写,比较字符串实际上就是比较每个字符串的内容。可以对自己的类进行这样的操作。对于结构,==运算符在默认状态下不做任何工作。试图比较两个结构,看看它们是否相等,就会产生一个编译错误,除非显式重载了==,告诉编译器如何进行比较。
在许多情况下,重载运算符允许生成可读性更高、更直观的代码,包括:
l 在数学领域中,几乎包括所有的数学对象:坐标、矢量、矩阵、张量和函数等。如果编写一个程序执行某些数学或物理建模,肯定会用类表示这些对象。
l 图形程序在计算屏幕上的位置时,也使用数学或相关的坐标对象。
l 表示大量金钱的类(例如,在财务程序中)。
l 字处理或文本分析程序也有表示语句、子句等的类,可以使用运算符把语句连接在一起(这是字符串连接的一种比较复杂的版本)。
下表展示了所有可以重载和不可以重载的运算符:
可被重载的 |
|
一元运算符 |
+, -, !, ~, ++, --, true, false 注意:true和false运算符必须成对重载 |
二元运算符 |
+, -, *, /, %, &, |, ^, <<, >> |
关系运算符 |
==, !=, <, >, <=, >= 注意:必须成对重载 |
不可被重载的 |
|
条件运算符 |
&&, || |
数组运算符 |
[],但可以定义索引器。 |
转换运算符 |
(),但可以定义隐式类型转换和显式类型转换运算符。 |
赋值运算符 |
+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=,但重载相关的二元运算符,它们也就具备了同样的新功能 |
其他运算符 |
=, ., ?:, ->, new, is, sizeof, typeof |
C#要求成对重载比较运算符。如果重载了==,也必须重载!=,否则会产生编译错误。另外,比较运算符必须返回bool类型的值。这是它们与算术运算符的根本区别。两个数相加或相减的结果,理论上取决于数的类型。而两个Vector的相乘会得到一个标量。另一个例子是.NET基类System.DateTime,两个DateTime实例相减,得到的结果不是DateTime,而是一个System.TimeSpan实例,但比较运算得到的如果不是bool类型的值,就没有任何意义。
另外,有许多类与运算符重载并不相关。不恰当地使用运算符重载,会使使用类型的代码很难理解。例如,把两个DateTime对象相乘,在概念上没有任何意义。
本文章为天轰穿原创作品,转载请注明出处及作者。