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

97

ambas as interfaces parecem comparar objetos por igualdade, então quais são as principais diferenças entre elas?

SoftwareGeek
fonte

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.

Greg D
fonte
20

IEquatable<T> para a igualdade.

IComparable<T> para fazer o pedido.

Islam Yahiatene
fonte
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).

Damien_The_Unbeliever
fonte
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

Will Eddins
fonte
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;
        }
    }
}
Nipuna
fonte
1
Obrigado por esta ótima explicação. Pergunta: por que IEquatableusa um genérico <Person>e IComparablenão?
veuncent