O exercício de programação ilustra uma série de propriedades da igualdade em C#:
Object.Equals()== e !=. Isso é considerado mais idiomático do que usar o método Equals(), que também está disponível com esses tipos. Quem programa em Java deve ficar atento, ao lidar com strings, ao fato de que == compara por valor em C#, mas por referência em Java, ao voltar à sua antiga linguagem.Equals() herdado de object. Se o seu objetivo com o teste de igualdade é garantir que dois objetos são exatamente a mesma instância, confiar na implementação de object será suficiente. Caso contrário, você precisa sobrescrever object.Equals().Equals() precisa ser sobrescrito.Equals() sobrescrito conterá testes de igualdade em membros de tipos simples usando == e em tipos de referência com chamadas recursivas a Equals().class StatusBar
{
private readonly int width = 200, height = 20;
public override bool Equals(object other)
{
// ... null and type checks and performance optimisations
return width == (other as StatusBar).width && height == (other as StatusBar).height;
}
}
class Window
{
private readonly string title = "Main";
private readonly StatusBar statusBar = new StatusBar();
public override bool Equals(object other)
{
// ... null and type checks and performance optimisations
return title == (other as Window).title && statusBar.Equals((other as Window).statusBar);
}
}
object.ReferenceEquals() é usado para comparar dois objetos e detectar se são uma única e mesma instância. Isso traz clareza e é uma necessidade quando Equals() e == foram sobrescritos/sobrecarregados.var winA = new Window(); // above code shows that all windows are equal
var winB = new Window();
ReferenceEquals(winA, winB);
// => false
var winC = winA;
ReferenceEquals(winA, winC);
// => true
public override bool Equals(object obj), as IDEs normalmente geram a sobrecarga protected bool Equals(FacialFeatures other) para uso quando há herança envolvida. Uma classe derivada pode chamar o Equals() da classe base e depois adicionar seu próprio teste.== a menos que você tenha sobrecarregado o operador ==, bem como o método Equals() na sua classe (veja o exercício operator-overloading), ou a menos que você se importe apenas que as referências sejam iguais.structs são tratados no exercício structs.tuples é tratada no exercício tuples.protected bool Equals(T other)
{
return field1 == other.field1 && field2.Equals(other.field2);
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != this.GetType()) return false;
return Equals((T) obj);
}
SequenceEquals(), mas, na ausência do LINQ, é uma questão de iterar pelas duas coleções e comparar os itens individualmente.== e != com suas próprias classes, veja o exercício operator-overloading.Object.GetHashCode()object.GetHashCode() retorna um código hash na forma de um inteiro de 32 bits. O código hash é usado por classes de dicionário e de conjunto, como Dictionary<T> e HashSet<T>, para armazenar e recuperar objetos de forma performática. No caso dos dicionários, o hashing está relacionado às chaves.Equals(), também sobrescreverá GetHashCode(). Existe uma relação entre Equals() e GetHashCode() que precisa se manter verdadeira para o comportamento correto das classes de dicionário e de conjunto hash e de quaisquer outras que usem um código hash. Espera-se que você implemente o método de modo que não crie armadilhas para quem for dar manutenção e talvez adicione uma coleção baseada em código hash mais tarde.Equal() retorna true), então o GetHashCode() dos dois objetos precisa retornar o mesmo valor. Isso não vale na direção inversa. Não é simétrico. Imagine uma função de busca que primeiro vai a um "bucket" com base no código hash e depois seleciona o item específico usando o teste de igualdade.HashCode.Combine() passando os valores usados no teste de igualdade (ou um subconjunto deles). Tenha em mente que, quanto mais informações você fornecer a Combine(), provavelmente mais performática será a implementação do hash.public class Assessment
{
private int rating;
private Person boss;
public override int GetHashCode()
{
return HashCode.Combine(rating, boss);
}
}
Para melhorar um pouco o desempenho, especialmente quando os objetos pertencem a coleções, você pode adicionar um membro sobrecarregado public bool Equals(T other).
Isso economizará uma certa quantidade de verificação de tipo para tipos de referência e eliminará uma etapa de boxing para tipos de valor, já que eles não precisarão ser convertidos em um objeto (boxed) como argumento para public override bool Equals(object other).
Se você adicionar a interface IEquatable<T> à sua classe, isso exigirá que a sobrecarga seja implementada. A menos que seu código contenha rotinas que recebam objetos do tipo IEquatable<T> (e provavelmente façam algo interessante relacionado à igualdade, independentemente da classe que a implementa), não há realmente nenhum outro motivo convincente para incluir a interface.
IEqualityComparer<T>Se você tem uma classe que pode ser identificada unicamente de duas maneiras diferentes, digamos uma classe Person que tem um SSID e um endereço de email único, então o .NET fornece um meio de permitir que duas coleções diferentes usem testes de código hash e de igualdade diferentes. Cada uma pode ter uma implementação diferente de IEqualityComparer<T>, com seu próprio método Equals() e GetHashCode(). Você pode ter um dicionário com chave por SSID e outro com chave por endereço de email.
Onde IEqualityComparer<T> está em jogo, você normalmente ainda sobrescreveria Equals() e GetHashCode() na sua classe de item, para evitar problemas fora das classes de coleção.
Uma consideração ao usar IEqualityComparer<T> é que métodos privados etc. não estarão disponíveis para o teste de igualdade.
Se apenas uma coleção com hash estiver em jogo, pode ser melhor evitar IEqualityComparer<T> e encapsular a igualdade e o código hash no próprio objeto. Não é ideal, em primeiro lugar, que uma dependência-chave como a que existe entre o objeto e a coleção com hash não possa ser imposta pelo compilador, mas, com a lógica de hashing e comparação em outro lugar, seria ainda mais fácil para quem for dar manutenção alterar os membros de uma classe sem considerar as consequências para a coleção.
Um primitivo que pode desafiar quem programa sem estar atento é o teste da igualdade de valores de ponto flutuante. Isso é discutido no documento about.md do conceito floating-point-numbers.
Este artigo mostra algumas das decisões que precisam ser tomadas em relação à igualdade e à herança. Elas só dirão respeito a você se você estiver manipulando instâncias de uma classe e de uma classe derivada e precisar testar a igualdade entre as duas.