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.