Enquanto eu aprendia C #, descobri que o C # suporta sobrecarga do operador. Eu tenho problema com um bom exemplo que:
- Faz sentido (por exemplo, adicionar classe chamada ovelha e vaca)
- Não é um exemplo de concatenação de duas cadeias
Exemplos da Base Class Library são bem-vindos.
==
para fazer a multiplicação, faz sentido para mim, mas pode não fazer sentido para os outros! Trata-se de legitimidade de quais linguagens de programação de instalações ou estamos falando sobre 'práticas recomendadas de codificação'?Respostas:
Os exemplos óbvios de sobrecarga adequada do operador são quaisquer classes que se comportam da mesma maneira que os números operam. Portanto, as classes BigInt (como sugere Jalayn ), os números complexos ou as classes matriciais (como sugere Superbest ), todas têm as mesmas operações que os números comuns, portanto, mapeiam muito bem os operadores matemáticos, enquanto as operações de tempo (como sugerido por svick ) mapeiam muito bem um subconjunto dessas operações.
Um pouco mais abstratamente, os operadores podem ser usados ao executar operações definidas , assim,
operator+
podem ser uma união ,operator-
podem ser um complemento etc. Isso começa a esticar o paradigma, especialmente se você usar o operador de adição ou multiplicação para uma operação que não é ' t comutativa , como você pode esperar que eles sejam.O próprio C # possui um excelente exemplo de sobrecarga não numérica do operador. Ele usa
+=
e-=
adiciona e subtrai delegados , ou seja, registra e cancela o registro. Isso funciona bem porque os operadores+=
e-=
funcionam como você esperaria, e isso resulta em um código muito mais conciso.Para o purista, um dos problemas com o
+
operador de string é que ele não é comutativo."a"+"b"
não é o mesmo que"b"+"a"
. Entendemos essa exceção para strings porque é muito comum, mas como podemos saber se o usooperator+
em outros tipos será comutativo ou não? A maioria das pessoas assume que sim, a menos que o objeto seja do tipo string , mas você nunca sabe realmente o que as pessoas assumirão.Como nas cordas, os pontos fracos das matrizes também são bastante conhecidos. É óbvio que
Matrix operator* (double, Matrix)
é uma multiplicação escalar, ao passo queMatrix operator* (Matrix, Matrix)
seria uma multiplicação de matrizes (por exemplo, uma matriz de multiplicações de produtos pontuais).Da mesma forma, o uso de operadores com delegados está tão obviamente distante da matemática que é improvável que você cometa esses erros.
Aliás, na conferência da ACCU de 2011 , Roger Orr e Steve Love apresentaram uma sessão sobre Alguns objetos são mais iguais que outros - uma olhada nos muitos significados de igualdade, valor e identidade . Seus slides podem ser baixados , assim como o Apêndice de Richard Harris sobre igualdade de ponto flutuante . Resumo: Tenha muito cuidado com
operator==
, aqui estão os dragões!A sobrecarga do operador é uma técnica semântica muito poderosa, mas é fácil de usar em excesso. Idealmente, você deve usá-lo apenas em situações em que o contexto de um operador sobrecarregado seja muito claro. De muitas maneiras
a.union(b)
é mais clara do quea+b
, ea*b
é muito mais obscura do quea.cartesianProduct(b)
, especialmente desde que o resultado de um produto cartesiano seria umSetLike<Tuple<T,T>>
em vez de umSetLike<T>
.Os problemas reais com sobrecarga do operador ocorrem quando um programador assume que uma classe se comportará de uma maneira, mas na verdade se comporta de outra. Esse tipo de conflito semântico é o que estou sugerindo que é importante tentar evitar.
fonte
d1 + d2
por dois delegados do mesmo tipo.Estou surpreso que ninguém tenha mencionado um dos casos mais interessantes no BCL:
DateTime
eTimeSpan
. Você pode:TimeSpan
s para obter outroTimeSpan
TimeSpan
para obter um negadoTimeSpan
DateTime
s para obter umaTimeSpan
TimeSpan
de umDateTime
para obter outroDateTime
Outro conjunto de operadores que podem fazer sentido em um monte de tipos são
<
,>
,<=
,>=
. No BCL, por exemplo, osVersion
implementa.fonte
O primeiro exemplo que me vem à mente é a implementação do BigInteger , que permite trabalhar com números inteiros grandes assinados. Confira o link do MSDN para ver quantos operadores foram sobrecarregados (ou seja, há uma grande lista e eu não verifiquei se todos os operadores foram sobrecarregados, mas certamente parece que sim)
Além disso, como eu também faço Java e Java não permite sobrecarregar operadores, é incrivelmente mais agradável escrever
Than, em Java:
fonte
Fico feliz por ter visto isso porque ando brincando com o Irony e ele tem um ótimo uso de sobrecarga de operador. Aqui está uma amostra do que ele pode fazer.
Portanto, o Irony é um "Kit de Implementação de Linguagem .NET" e é um gerador de analisador (gerando um analisador LALR). Em vez de ter que aprender uma nova sintaxe / linguagem como geradores de analisador como o yacc / lex, você escreve a gramática em C # com a sobrecarga do operador. Aqui está uma gramática BNF simples
Portanto, é uma gramática simples (desculpe-a se houver inconsistências, pois estou apenas aprendendo BNF e construindo gramáticas). Agora vamos ver o c #:
Como você pode ver, com a sobrecarga do operador, escrever a gramática em C # é quase exatamente escrever a gramática em BNF. Para mim, isso não apenas faz sentido, mas também é um ótimo uso da sobrecarga do operador.
fonte
O exemplo principal é operator == / operator! =.
Se você deseja comparar facilmente dois objetos com valores de dados em vez de por referência, sobrecarregará .Equals (e.GetHashCode!) E poderá fazer os operadores! = E == também para obter consistência.
Eu nunca vi nenhuma sobrecarga selvagem de outros operadores em C # (eu imagino que há casos extremos onde isso pode ser útil).
fonte
Este exemplo do MSDN mostra como implementar números complexos e fazer com que eles usem o operador + normal.
Outro exemplo mostra como fazer isso para adição de matriz e também explica como não usá-lo para adicionar um carro a uma garagem (leia o link).
fonte
O bom uso da sobrecarga pode ser raro, mas acontece.
operador de sobrecarga == e operador! = mostram duas escolas de pensamento: as que dizem que isso facilita as coisas e as que dizem que impede a comparação de endereços (ou seja, estou apontando exatamente para o mesmo lugar na memória, não apenas uma cópia do mesmo objeto).
Acho que as sobrecargas do operador de elenco são úteis em situações específicas. Por exemplo, eu tive que serializar / desserializar em XML um booleano representado como 0 ou 1. O operador certo (implícito ou explícito, esqueço) de conversão de booleano para int e back fez o truque.
fonte
object.ReferenceEquals()
.==
lançando:(object)foo == (object)bar
sempre compara referências. Mas eu prefeririaReferenceEquals()
, como o @ dan04 menciona, porque é mais claro o que faz.Eles não fazem parte da categoria em que as pessoas normalmente pensam quando sobrecarregam o operador, mas acho que um dos operadores mais importantes que podem sobrecarregar é o operador de conversão .
Os operadores de conversão são especialmente úteis para tipos de valor que podem "descodificar" para um tipo numérico ou que podem agir como um tipo numérico em alguns contextos. Por exemplo, você pode definir um
Id
tipo especial que representa um determinado identificador e fornecer uma conversão implícita para,int
para que você possa passar umId
para um método que leva umaint
, mas uma conversão explícita deint
paraId
para que ninguém possa passar umint
para um método que leva umId
sem lançá-lo primeiro.Como um exemplo fora do C #, a linguagem Python inclui muitos comportamentos especiais que são implementados como operadores sobrecarregáveis. Isso inclui o
in
operador para teste de associação, o()
operador para chamar um objeto como se fosse uma função e olen
operador para determinar o comprimento ou tamanho de um objeto.E então você tem linguagens como Haskell, Scala e muitas outras linguagens funcionais, onde nomes como
+
são apenas funções comuns e não são operadores (e existe suporte de linguagem para o uso de funções na posição de infix).fonte
O Point Struct no espaço para nome System.Drawing usa sobrecarga para comparar dois locais diferentes usando sobrecarga do operador.
Como você pode ver, é muito mais fácil comparar as coordenadas X e Y de dois locais usando a sobrecarga.
fonte
Se você estiver familiarizado com o vetor matemático, poderá ser útil sobrecarregar o
+
operador. Você pode adicionar um vetora=[1,3]
comb=[2,-1]
e obterc=[3,2]
.Sobrecarregar os iguais (==) também pode ser útil (mesmo que seja provavelmente melhor implementar um
equals()
método). Para continuar os exemplos de vetores:fonte
Imagine um pedaço de código para desenhar em um formulário
Outro exemplo comum é quando uma estrutura é usada para armazenar informações de posição na forma de um vetor.
apenas para ser usado mais tarde como
fonte
operator+
deve ser sobrecarregado (você pode implementar um ponto em termos de vetor, mas não deve poder adicionar dois pontos)p1+((p2-p1)+(p3-p1)+(p4-p1))/4
, mas isso parece um pouco estranho.