Sobrecargas de operador

Observação

Este conteúdo é reimpresso com permissão da Pearson Education, Inc. de Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries, 2nd Edition. Essa edição foi publicada em 2008 e, desde então, o livro foi totalmente revisado na terceira edição. Algumas das informações nesta página podem estar desatualizadas.

As sobrecargas de operador permitem que os tipos de estrutura apareçam como se fossem primitivos de linguagem internos.

Embora permitido e útil em algumas situações, as sobrecargas do operador devem ser usadas com cautela. Em muitos casos, houve abuso da sobrecarga de operador, como quando designers de estrutura começaram a usar operadores para operações que deveriam ser métodos simples. As diretrizes a seguir devem ajudá-lo a decidir quando e como usar a sobrecarga de operador.

❌ EVITE definir sobrecargas de operador, exceto em tipos que devem parecer tipos primitivos (internos).

✔️ CONSIDERE definir sobrecargas de operador em um tipo que deve parecer um tipo primitivo.

Por exemplo, System.String tem operator== e operator!= definidos.

✔️ DEFINA sobrecargas de operador em structs que representam números (como System.Decimal).

❌ NÃO seja engraçadinho ao definir sobrecargas de operador.

A sobrecarga de operador é útil nos casos em que é imediatamente óbvio qual será o resultado da operação. Por exemplo, faz sentido conseguir subtrair um DateTime de outro DateTime e obter um TimeSpan. No entanto, não é apropriado usar o operador de união lógica para unir duas consultas de banco de dados ou usar o operador shift para gravar em um fluxo.

❌ NÃO forneça sobrecargas de operador, a não ser que pelo menos um dos operandos seja do tipo que define a sobrecarga.

✔️ SOBRECARREGUE operadores de maneira simétrica.

Por exemplo, se você sobrecarregar o operator==, também deverá sobrecarregar o operator!=. Da mesma forma, se você sobrecarregar o operator<, também deverá sobrecarregar o operator> e assim por diante.

✔️ CONSIDERE fornecer métodos com nomes amigáveis que correspondem a cada operador sobrecarregado.

Muitas linguagens não dão suporte à sobrecarga de operador. Por esse motivo, é recomendável que os tipos que sobrecarregam os operadores incluam um método secundário com um nome específico do domínio apropriado que fornece funcionalidade equivalente.

A tabela a seguir contém uma lista de operadores e os nomes de método amigáveis correspondentes.

Símbolo do operador C# Nome dos metadados Nome amigável
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

Operador de sobrecarga ==

Sobrecarregar operator == é bastante complicado. A semântica do operador precisa ser compatível com vários outros membros, como Object.Equals.

Operadores de conversão

Os operadores de conversão são operadores unários que permitem a conversão de um tipo em outro. Os operadores devem ser definidos como membros estáticos no operando ou no tipo de retorno. Há dois tipos de operadores de conversão: implícito e explícito.

❌ NÃO forneça um operador de conversão se essa conversão não for claramente esperada pelos usuários finais.

❌ NÃO defina operadores de conversão fora do domínio de um tipo.

Por exemplo, Int32, Double e Decimal são todos os tipos numéricos, enquanto DateTime, não. Portanto, não deve haver nenhum operador de conversão para converter um Double(long) em um DateTime. Um construtor é preferido nesse caso.

❌ NÃO forneça um operador de conversão implícita se a conversão potencialmente tiver perda.

Por exemplo, não deve haver uma conversão implícita de Double em Int32 porque Double tem um intervalo maior do que Int32. Um operador de conversão explícita pode ser fornecido mesmo que a conversão potencialmente tenha perda.

❌ NÃO gere exceções de conversões implícitas.

É muito difícil para os usuários finais entenderem o que está acontecendo, pois eles podem não estar cientes de que uma conversão está ocorrendo.

✔️ GERE System.InvalidCastException se uma chamada para um operador de conversão resultar em uma conversão de perda e o contrato do operador não permitir conversões com perda.

Portions © 2005, 2009 Microsoft Corporation. Todos os direitos reservados.

Reimpresso com permissão da Pearson Education, Inc. das Diretrizes de Design do Framework: convenções, linguagens e padrões para bibliotecas do .NET reutilizável, 2ª edição por Krzysztof Cwalina e Brad Abrams, publicado em 22 de outubro de 2008 por Addison-Wesley Professional como parte da série de desenvolvimento do Microsoft Windows.

Confira também