O compilador C # exige que sempre que um tipo personalizado defina operador ==, ele também deve definir !=(veja aqui ).
Por quê?
Estou curioso para saber por que os designers consideraram necessário e por que o compilador não pode usar como padrão uma implementação razoável para um dos operadores quando apenas o outro está presente. Por exemplo, Lua permite definir apenas o operador de igualdade e você obtém o outro gratuitamente. O C # pode fazer o mesmo solicitando que você defina == ou ambos == e! = E depois compile automaticamente o operador! = Ausente como !(left == right).
Entendo que existem casos de canto estranhos em que algumas entidades podem não ser iguais nem desiguais (como as NaN da IEEE-754), mas essas parecem ser a exceção, não a regra. Portanto, isso não explica por que os designers do compilador C # fizeram da exceção a regra.
Eu já vi casos de mão de obra deficiente em que o operador de igualdade é definido, então o operador de desigualdade é um copiar-colar com todas as comparações revertidas e todos os&& alternados para um || (você entendeu ... basicamente! (a == b) expandido pelas regras de De Morgan). Essa é uma prática ruim que o compilador poderia eliminar por design, como é o caso de Lua.
Nota: O mesmo vale para os operadores <> <=> =. Não consigo imaginar casos em que você precisará defini-los de maneiras não naturais. Lua permite definir apenas <e <= e define> = e> naturalmente através da negação dos formadores. Por que o C # não faz o mesmo (pelo menos 'por padrão')?
EDITAR
Aparentemente, existem razões válidas para permitir que o programador implemente verificações de igualdade e desigualdade da maneira que desejar. Algumas das respostas apontam para casos em que isso pode ser bom.
O núcleo da minha pergunta, no entanto, é por que isso é exigido à força no C # quando normalmente não é logicamente necessário?
Também está em contraste impressionante com as opções de design para interfaces .NET como Object.Equals, IEquatable.Equals IEqualityComparer.Equalsonde a falta de uma NotEqualscontrapartida mostra que a estrutura considera os !Equals()objetos como desiguais e é isso. Além disso, classes Dictionarye métodos como .Contains()dependem exclusivamente das interfaces mencionadas acima e não usam os operadores diretamente, mesmo que sejam definidos. De fato, quando o ReSharper gera membros de igualdade, ele define ambos ==e !=em termos de, Equals()e mesmo assim, somente se o usuário optar por gerar operadores. Os operadores de igualdade não são necessários pela estrutura para entender a igualdade de objetos.
Basicamente, a estrutura .NET não se preocupa com esses operadores, apenas se preocupa com alguns Equalsmétodos. A decisão de exigir que os operadores == e! = Sejam definidos em conjunto pelo usuário está relacionada exclusivamente ao design da linguagem e não à semântica de objetos no que diz respeito ao .NET.