O exercício de programação ilustra várias propriedades da igualdade em C#:
Object.Equals()== e !=. Isto é considerado mais idiomático do que usar o método Equals(), que também está disponível com estes tipos. Os programadores de Java devem ter atenção, ao lidar com strings, ao facto de == comparar por valor em C# mas por referência em Java quando voltarem à sua linguagem anterior.Equals() herdado de object. Se o teu objetivo com o teste de igualdade é garantir que dois objetos são exatamente a mesma instância, basta confiar na implementação de object. Caso contrário, tens de substituir object.Equals().Equals() tem de ser substituído.Equals() substituído conterá testes de igualdade sobre membros de tipos simples com == e sobre 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 detetar se são uma e a mesma instância. Isto traz clareza e é uma necessidade quando Equals() e == foram substituídos/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 geram normalmente a sobrecarga protected bool Equals(FacialFeatures other) para usar quando está envolvida herança. Uma classe derivada pode chamar o Equals() da classe base e depois acrescentar o seu próprio teste.== a não ser que tenhas sobrecarregado o operador ==, bem como o método Equals() na tua classe (ver o exercício operator-overloading) ou só te interesse que as referências sejam iguais.structs são abordados no exercício structs.tuples é abordada 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 de LINQ é uma questão de iterar por ambas as coleções e comparar os itens individualmente.== e != com as tuas próprias classes, vê o exercício operator-overloading.Object.GetHashCode()object.GetHashCode() devolve um código de hash sob a forma de um número inteiro de 32 bits. O código de hash é usado por classes de dicionário e de conjunto, como Dictionary<T> e HashSet<T>, para armazenar e obter objetos de forma eficiente. No caso dos dicionários, o hashing está relacionado com as chaves.Equals(), também substituirás GetHashCode(). Há uma relação entre Equals() e GetHashCode() que tem de se verificar para o comportamento correto das classes de dicionário e de conjunto de hash e de quaisquer outras que usem um código de hash. Espera-se que implementes o método de modo a não deixar armadilhas para os responsáveis pela manutenção que possam acrescentar mais tarde uma coleção baseada em códigos de hash.Equal() devolve true), então GetHashCode() para os dois objetos tem de devolver o mesmo valor. Isto não se aplica na direção inversa. Não é simétrico. Imagina uma função de pesquisa que primeiro vai a um "bucket" com base no código de hash e depois escolhe o item em particular usando o teste de igualdade.HashCode.Combine(), passando os valores usados no teste de igualdade (ou um subconjunto deles). Tem em conta que, quanta mais informação forneceres ao Combine(), mais eficiente será provavelmente 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 ligeiramente o desempenho, especialmente quando os objetos pertencem a coleções, podes acrescentar um membro sobrecarregado public bool Equals(T other).
Isto poupará uma certa quantidade de verificação de tipos para os tipos de referência e poupará um passo de boxing para os tipos de valor, uma vez que não precisarão de ser convertidos num objeto (boxed) como argumento de public override bool Equals(object other).
Se acrescentares a interface IEquatable<T> à tua classe, isso exigirá que a sobrecarga seja implementada. A menos que o teu código contenha rotinas que recebam objetos do tipo IEquatable<T> (e que presumivelmente façam algo interessante relacionado com a igualdade, independentemente da classe que a implementa), não há realmente qualquer outra razão convincente para incluir a interface.
IEqualityComparer<T>Se tiveres uma classe que pode ser identificada de forma única de duas maneiras diferentes, por exemplo, 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 de hash e de igualdade diferentes. Cada uma pode ter uma implementação diferente de IEqualityComparer<T>, com o seu próprio método Equals() e GetHashCode(). Podes ter um dicionário com chave no SSID e outro com chave no endereço de email.
Quando IEqualityComparer<T> está em jogo, normalmente continuarias a substituir Equals() e GetHashCode() na tua classe de item para evitar problemas fora das classes de coleção.
Uma consideração a ter ao usar IEqualityComparer<T> é que os 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 de hash no próprio objeto. Não é ideal, desde logo, que uma dependência fundamental 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 de comparação noutro sítio, seria ainda mais fácil para um responsável pela manutenção fazer alterações aos membros de uma classe sem ter em conta as consequências para a coleção.
Um primitivo que pode desafiar o programador incauto é testar a igualdade de valores de vírgula flutuante. Isto é abordado no documento about.md do conceito floating-point-numbers.
Este artigo mostra algumas das decisões que têm de ser tomadas em relação à igualdade e à herança. Só te dirão respeito se estiveres a manipular instâncias de uma classe e de uma classe derivada e precisares de testar a igualdade entre as duas.