Qual é a diferença entre interfaces IComparable e IEquatable?


Respostas:


188

IEquatable testa se dois objetos são iguais.

IComparable impõe uma ordem total aos objetos comparados.

Por exemplo, IEquatablediria que 5 não é igual a 7. IComparablediria que 5 vem antes de 7.



10

Além da resposta de Greg D:

Você pode implementar IComparablesem implementar IEquatablepara uma classe em que uma ordem parcial faça sentido e onde você definitivamente deseja que o consumidor deduza que, só porque CompareTo()retorna zero, isso não significa que os objetos são iguais (para qualquer coisa diferente de fins de classificação).


10
Isso soa muito mais como um comparador de caso especial do que como um objeto implementado IComparablecorretamente. Você pode dar um exemplo significativo em CompareTo(…) == 0que não implique igualdade? Certamente não posso. Na verdade, o contrato de interface (conforme MSDN) exige que CompareTo(…) == 0implique igualdade. Para ser franco, em um caso como o seu, use um Comparatorobjeto especial , não implemente IComparable.
Konrad Rudolph,

2
@Konrad - eu indiquei várias advertências - que o tipo não implementa IEquatable (então, obviamente, o originador não deseja incluir um teste de igualdade) e que os resultados CompareTo são usados ​​para classificação, não para avaliar igualdade. Você também se questiona sobre qual igualdade é relevante (referência, valor, ignorando atributos "arbitrários" - um livro azul de 500 páginas de comprimento pode ser "igual" a um livro vermelho de 500 páginas, para fins de IComparável)
Damien_The_Unbeliever,

4
Sua última frase está errada, e este é o erro específico que eu queria apontar: IComparableé totalmente inapropriado aqui. O que você tem é uma ordem muito particular que se aplica apenas a uma situação especial. Para tais situações, implementar um general IComparableé errado. É para isso que IComparerexistem. Por exemplo, as pessoas não podem ser ordenadas de forma significativa. Mas podem ser encomendados de acordo com o salário, o tamanho do sapato, a quantidade de sardas ou o peso. Portanto, implementaríamos IComparers diferentes para todos esses casos.
Konrad Rudolph,

2
@Konrad Rudolph: Que tal algo como uma classe "ScheduledEvent", que deveria fazer "algo" em um determinado momento? A semântica do tipo implicaria em uma ordem semântica natural muito forte com base em quando a ação deveria ocorrer, mas pode-se facilmente ter diferentes eventos ocorrendo ao mesmo tempo. Pode-se exigir o uso de um IComparer especificado manualmente, mas eu diria que ter um comparador embutido na classe seria mais conveniente.
supercat

4
@supercat A conveniência é importante, mas não é tudo. A correção (como em consistência lógica) é mais importante e o sistema de tipo estático é uma ferramenta importante para verificar essa consistência lógica. Ao violar o contrato documentado de interfaces que você implementa, você está subvertendo o sistema de tipos. Esta não é uma boa ideia e nunca a recomendaria. Use um comparador externo para tais situações.
Konrad Rudolph

7

Conforme declarado na Página do MSDN para IEquatable :

A interface IComparable define o CompareTométodo, que determina a ordem de classificação das instâncias do tipo de implementação. A interface IEquatable define o Equalsmétodo, que determina a igualdade das instâncias do tipo de implementação.

Equals vs. CompareTo


2

IComparable <T> define um método de comparação específico de tipo que pode ser usado para ordenar ou classificar objetos.

IEquatable <T> define um método generalizado que pode ser usado para implementar para determinar a igualdade.


Digamos que você tenha uma classe Person

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

Person p1 = new Person() { Name = "Person 1", Age = 34 };
Person p2 = new Person() { Name = "Person 2", Age = 31 };
Person p3 = new Person() { Name = "Person 3", Age = 33 };
Person p4 = new Person() { Name = "Person 4", Age = 26 };

List<Person> people = new List<Person> { p1, p2, p3, p4 };

Para classificar esses objetos, você pode usar people.Sort();.

Mas isso lançará uma exceção.

insira a descrição da imagem aqui

O Framework não sabe como classificar esses objetos. Você precisa dizer como classificar a IComparableinterface de implementação .

public class Person : IComparable
{
    public string Name { get; set; }
    public int Age { get; set; }

    public int CompareTo(object obj)
    {
        Person otherPerson = obj as Person;
        if (otherPerson == null)
        {
            throw new ArgumentNullException();
        }
        else
        {
            return Age.CompareTo(otherPerson.Age);
        }
    }
}

Isso classificará a matriz corretamente com o Sort()método.


Em seguida, para comparar dois objetos, você pode usar o Equals()método.

var newPerson = new Person() { Name = "Person 1", Age = 34 };
var newPersonIsPerson1 = newPerson.Equals(p1);

Isso retornaráfalse porque o Equalsmétodo não sabe como comparar dois objetos. Portanto, você precisa implementar a IEquatableinterface e dizer ao framework como fazer a comparação. Estendendo o exemplo anterior, ficará assim.

public class Person : IComparable, IEquatable<Person>
{
    //Some code hidden

    public bool Equals(Person other)
    {
        if (Age == other.Age && Name == other.Name)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

1
Obrigado por esta ótima explicação. Pergunta: por que IEquatableusa um genérico <Person>e IComparablenão?
veuncent
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.