运算符重载

注意

此内容根据 Pearson Education, Inc. 许可转载自《框架设计指南:可重用 .NET 库的约定、习语和模式第二版》。 该版本于 2008 年出版,并在此后于第三版对该书进行了全面修订。 此页上的一些信息可能已过时。

运算符重载可以使框架类型看起来像是内置语言基元一样。

尽管运算符重载在某些情况下是允许使用的,而且很有用,但还是应该谨慎使用它们。 在许多情况下,运算符重载被滥用,例如当框架设计者开始使用运算符来执行本应是简单方法的运算时。 以下准则应该可帮助你决定何时以及如何使用运算符重载。

❌ 请避免定义运算符重载,除非在感觉像是基元(内置)类型的类型中。

✔️ 请考虑在一个感觉像是基元类型的类型中定义运算符重载。

例如,System.String 定义了 operator==operator!=

✔️ 请务必在表示数字的结构(如 System.Decimal)中定义运算符重载。

❌ 在定义运算符重载时请勿太刻意。

运算符重载在运算结果显而易见的情况下非常有用。 例如,能够从另一个 DateTime 中减去一个 DateTime 并得到一个 TimeSpan,这是有意义的。 但是,以下操作是不恰当的:使用逻辑 union 运算符来联合两个数据库查询或使用 shift 运算符写入到流中。

❌ 除非至少有一个操作数属于定义重载的类型,否则请勿提供运算符重载。

✔️ 请确保以对称方式重载运算符。

例如,如果你重载了 operator==,则还应重载 operator!=。 同样,如果你重载了 operator<,则还应重载 operator>,诸如此类。

✔️ 请考虑为方法提供与每个重载的运算符相对应的友好名称。

许多语言不支持运算符重载。 出于此原因,建议重载运算符的类型包含一个辅助方法,该方法具有适当的特定于域的名称且提供等效功能。

下表包含一个运算符列表及相应的友好方法名称。

C# 运算符符号 元数据名称 友好名称
N/A op_Implicit To<TypeName>/From<TypeName>
N/A op_Explicit To<TypeName>/From<TypeName>
+ (binary) op_Addition Add
- (binary) op_Subtraction Subtract
* (binary) op_Multiply Multiply
/ op_Division Divide
% op_Modulus Mod or Remainder
^ op_ExclusiveOr Xor
& (binary) op_BitwiseAnd BitwiseAnd
| op_BitwiseOr BitwiseOr
&& op_LogicalAnd And
|| op_LogicalOr Or
= op_Assign Assign
<< op_LeftShift LeftShift
>> op_RightShift RightShift
N/A op_SignedRightShift SignedRightShift
N/A op_UnsignedRightShift UnsignedRightShift
== op_Equality Equals
!= op_Inequality Equals
> op_GreaterThan CompareTo
< op_LessThan CompareTo
>= op_GreaterThanOrEqual CompareTo
<= op_LessThanOrEqual CompareTo
*= op_MultiplicationAssignment Multiply
-= op_SubtractionAssignment Subtract
^= op_ExclusiveOrAssignment Xor
<<= op_LeftShiftAssignment LeftShift
%= op_ModulusAssignment Mod
+= op_AdditionAssignment Add
&= op_BitwiseAndAssignment BitwiseAnd
|= op_BitwiseOrAssignment BitwiseOr
, op_Comma Comma
/= op_DivisionAssignment Divide
-- op_Decrement Decrement
++ op_Increment Increment
- (unary) op_UnaryNegation Negate
+ (unary) op_UnaryPlus Plus
~ op_OnesComplement OnesComplement

重载运算符 ==

重载 operator == 非常复杂。 该运算符的语义需要与其他一些成员(如 Object.Equals)兼容。

转换运算符

转换运算符是允许从一种类型转换为另一种类型的一元运算符。 该运算符必须对操作数或返回类型定义为静态成员。 有两种类型的转换运算符:隐式和显式。

❌ 如果最终用户没有明确期望进行此类转换,请勿提供转换运算符。

❌ 请勿在类型的域外定义转换运算符。

例如,Int32DoubleDecimal 均为数值类型,而 DateTime 则不是。 因此,应该没有转换运算符能够将 Double(long) 转换为 DateTime。 在这种情况下,首选使用构造函数。

❌ 如果转换可能有损,请勿提供隐式转换运算符。

例如,不应存在从 DoubleInt32 的隐式转换,因为 Double 的范围比 Int32 大。 即使转换可能会有损,也可以提供显式转换运算符。

❌ 请勿从隐式强制转换引发异常。

最终用户很难了解发生的情况,因为他们可能没有意识到正在进行转换。

✔️ 如果对强制转换运算符的调用导致有损转换,而该运算符的协定不允许有损转换,请务必引发 System.InvalidCastException

Portions © 2005, 2009 Microsoft Corporation 版权所有。 保留所有权利。

在 Pearson Education, Inc. 授权下,由 Addison-Wesley Professional 作为 Microsoft Windows 开发系列的一部分再版自 Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries, 2nd Edition(Framework 设计准则:可重用 .NET 库的约定、惯例和模式第 2 版),由 Krzysztof Cwalina 和 Brad Abrams 发布于 2008 年 10 月 22 日。

请参阅