Qual é a diferença entre IEquatable e apenas substituindo Object.Equals ()?

185

Quero que minha Foodclasse seja capaz de testar sempre que for igual a outra instância de Food. Mais tarde vou usá-lo em uma lista e quero usar seu List.Contains()método. Devo implementar IEquatable<Food>ou simplesmente substituir Object.Equals()? Do MSDN:

Esse método determina a igualdade usando o comparador de igualdade padrão, conforme definido pela implementação do objeto IEquatable.Equals no método T (o tipo de valores na lista).

Então, minha próxima pergunta é: quais funções / classes da estrutura .NET fazem uso Object.Equals()? Devo usá-lo em primeiro lugar?

elísio devorado
fonte
3
Uma explicação muito boa aqui blogs.msdn.com/b/jaredpar/archive/2009/01/15/…
nawfal:
possível duplicata de IEquatable Entendimento
Nawfal

Respostas:

214

O principal motivo é o desempenho. Quando os genéricos foram introduzidos no .NET 2.0 eles foram capazes de adicionar um monte de aulas puro, como List<T>, Dictionary<K,V>, HashSet<T>, etc. Estas estruturas fazem uso pesado de GetHashCodee Equals. Mas para tipos de valor, isso requer boxe. IEquatable<T>permite que uma estrutura implemente um Equalsmétodo fortemente tipado , para que nenhum boxe seja necessário. Desempenho muito melhor ao usar tipos de valor com coleções genéricas.

Os tipos de referência não se beneficiam tanto, mas a IEquatable<T>implementação permite evitar uma conversão da System.Objectqual pode fazer diferença se for chamada com frequência.

Conforme observado no blog de Jared Parson , você ainda deve implementar as substituições de objetos.

Josh
fonte
Existe alguma conversão entre tipos de referência? Eu sempre pensei que elencos eram apenas "declarações" que você faz ao compilador quando atribui alguns elencos não óbvios de um tipo de objeto para outro. Isto é, depois de compilar, que o código nem saberia que havia uma conversão lá.
devorado elysium 29/04
7
Isso é verdade nas linguagens C ++, mas não nas .NET, que reforçam a segurança de tipos. Há uma conversão em tempo de execução e, se a conversão não for bem-sucedida, uma exceção será lançada. Portanto, há uma pequena penalidade de tempo de execução a ser paga pela transmissão. O compilador pode otimizar upcasts ausentes Por exemplo object o = (object) "string"; Mas downcasting - string s = (string) o; - deve acontecer em tempo de execução.
28410 Josh
1
Entendo. Por acaso, você tem algum lugar onde eu possa obter esse tipo de informação "mais profunda" sobre o .NET? Obrigado!
devorado elysium 29/04
7
Eu recomendaria o CLR via C # de Jeff Richter e C # in Depth de Jon Skeet. Quanto aos blogs, os blogs Wintellect são bons, blogs msdn etc.
Josh
A IEquatable<T>interface faz mais do que lembrar um desenvolvedor de incluir um public bool Equals(T other) membro na classe ou estrutura? A presença ou ausência da interface não faz diferença no tempo de execução. A sobrecarga de Equalsparece ser tudo o que é necessário.
mikemay 21/06
48

De acordo com o MSDN :

Se você implementar IEquatable<T>, também deverá substituir as implementações da classe base Object.Equals(Object)e GetHashCode para que seu comportamento seja consistente com o do IEquatable<T>.Equals método. Se você substituir Object.Equals(Object), sua implementação substituída também será chamada em chamadas para o Equals(System.Object, System.Object)método estático em sua classe. Isso garante que todas as invocações do Equalsmétodo retornem resultados consistentes.

Portanto, parece que não há diferença funcional real entre os dois, exceto que qualquer um pode ser chamado dependendo de como a classe é usada. Do ponto de vista do desempenho, é melhor usar a versão genérica porque não há penalidade de boxe / unboxing associada a ela.

Do ponto de vista lógico, também é melhor implementar a interface. Substituir o objeto realmente não diz a ninguém que sua classe é realmente equivalente. A substituição pode ser apenas uma classe do not nothing ou uma implementação superficial. Usar a interface diz explicitamente: "Ei, isso é válido para verificação de igualdade!" É apenas um design melhor.

CodexArcanum
fonte
9
As estruturas definitivamente devem implementar iEquatable (of theirOwnType) se forem usadas como chaves em um dicionário ou coleção semelhante; oferecerá um grande aumento de desempenho. Classes não-herdáveis ​​receberão um leve aumento de desempenho implementando IEquatable (of theirOwnType). Classes herdáveis ​​// não // implementam IEquatable.
Super dec
30

Estendendo o que Josh disse com um exemplo prático. +1 para Josh - eu estava prestes a escrever o mesmo na minha resposta.

public abstract class EntityBase : IEquatable<EntityBase>
{
    public EntityBase() { }

    #region IEquatable<EntityBase> Members

    public bool Equals(EntityBase other)
    {
        //Generic implementation of equality using reflection on derived class instance.
        return true;
    }

    public override bool Equals(object obj)
    {
        return this.Equals(obj as EntityBase);
    }

    #endregion
}

public class Author : EntityBase
{
    public Author() { }
}

public class Book : EntityBase
{
    public Book() { }
}

Dessa forma, tenho o método Equals () reutilizável que funciona imediatamente para todas as minhas classes derivadas.

isto. __curious_geek
fonte
Apenas mais uma pergunta. Qual é a vantagem de usar "obj como EntityBase" em vez de (EntityBase) obj? Apenas uma questão de estilo ou há alguma vantagem?
devorado elysium 29/04
22
no caso de "obj como EntityBase" - se obj não for do tipo EntityBase, ele passará "nulo" e continuará sem nenhum erro ou exceção. Mas no caso de "(EntityBase) obj", tentará forçá-lo para EntityBase e se o obj não for do tipo EntityBase, lançará InvalidCastException. E sim, "como" só pode ser aplicado a tipos de referência.
isso. __curious_geek
1
O link de Josh para o blog de Jared Par parece sugerir que você também precisa substituir o GetHashCode. Não é este o caso?
Amigável
3
Eu realmente não recebo o valor extra que sua implementação fornece. Você pode esclarecer o problema que sua classe base abstrata resolve?
Mert Akcakaya
1
@Amicable - sim, sempre que você substituir Object.Equals (Object), também deverá substituir GetHashCode para que os contêineres funcionem.
Namph
0

Se chamarmos object.Equals, isso força o boxe caro em tipos de valor. Isso é indesejável em cenários sensíveis ao desempenho. A solução é usar IEquatable<T>.

public interface IEquatable<T>
{
  bool Equals (T other);
}

A idéia por trás IEquatable<T>disso é que ele fornece o mesmo resultado, object.Equalsmas mais rapidamente. A restrição where T : IEquatable<T>deve ser usada com tipos genéricos, como abaixo.

public class Test<T> where T : IEquatable<T>
{
  public bool IsEqual (T a, T b)
  {
    return a.Equals (b); // No boxing with generic T
  }
}

caso contrário, ele se liga a slower object.Equals().

Seyedraouf Modarresi
fonte