Kiedy używać IComparable <T> Vs. IComparer <T>

Odpowiedzi:

96

Cóż, nie są one dokładnie tym samym, co IComparer<T>jest zaimplementowane w typie, który jest w stanie porównywać dwa różne obiekty, podczas gdy IComparable<T>jest zaimplementowany na typach, które są w stanie porównać się z innymi wystąpieniami tego samego typu.

Zwykle używam IComparable<T>w sytuacjach, gdy muszę wiedzieć, jak inna instancja odnosi się do thisinstancji. IComparer<T>przydaje się do sortowania kolekcji, ponieważ IComparer<T>znajduje się poza porównaniem.

Andrew Hare
źródło
9
IComparer <T> umożliwia również utworzenie klasy dla każdego typu, który chcesz. Przykład; PersonLastFirstNameComparer, PersonFirstLastNameComparer lub PersonAgeComparer.
Eric Schneider
Czy istnieje łatwy sposób ich zapamiętania? Za każdym razem muszę to sprawdzić.
amadib
59
@amadib myśleć IComparablejak jestem porównywalne . co oznacza, że ​​można mnie porównać do czegoś innego. A IComparerponieważ jestem osobą porównującą, po prostu porównuję, co oznacza, że ​​porównuję pewne rzeczy.
nawfal
@newfal Powinieneś był to podać jako odpowiedź. Myślę, że to najlepsze wyjaśnienie tutaj.
Gene S
42

Użyj, IComparable<T>gdy klasa ma wewnętrzne porównanie.

Użyj, IComparer<T>jeśli potrzebujesz metody porównania innej niż wewnętrzne porównanie klasy, jeśli takie ma.

yfeldblum
źródło
28

To zależy od jednostki. Na przykład w przypadku klasy takiej jak „Student” sensowne będzie posiadanie IComparable na podstawie nazwy.

class Student : IComparable 
{
    public string Name { get; set; }
    public int MathScore { get; set; }
    public int EnglishScore { get; set; }

    public int TotalScore 
    {
        get
        {
            return this.MathScore + this.EnglishScore; 
        }
    }

    public int CompareTo(object obj)
    {
        return CompareTo(obj as Student);  
    }

    public int CompareTo(Student other)
    {
        if (other == null)
        {
            return 1;
        }
        return this.Name.CompareTo(other.Name);  
    }
}

Ale jeśli nauczyciel „A” chce porównać uczniów na podstawie MathScore, a nauczyciel „B” chce porównać uczniów na podstawie EnglishScore. Dobrym pomysłem będzie oddzielne zaimplementowanie IComparer. (Bardziej jak wzorzec strategii)

class CompareByMathScore : IComparer<Student>
{
    public int Compare(Student x, Student y)
    {
        if (x.MathScore > y.MathScore)
          return 1;
        if (x.MathScore < y.MathScore)
          return -1;
        else
          return 0;
    }
}
Ajay Bhosale
źródło
Ustaw metodę porównania jako statyczną, aby była łatwa w użyciu.
stycznia
9

Wszystko zależy od tego, czy Twój typ jest zmienny, czy nie. Należy zaimplementować IComparable tylko na typach niemodyfikowalnych. Zauważ, że jeśli zaimplementujesz IComparable, musisz przesłonić Equals wraz z operatorami ==,! =, <I> (zobacz ostrzeżenie analizy kodu CA1036).

Cytując Dave'a G z tego wpisu na blogu :

Ale poprawną odpowiedzią jest zaimplementowanie IComparer zamiast IComparable, jeśli twoje obiekty są mutowalne, i przekazanie wystąpienia IComparer do funkcji sortujących, gdy jest to konieczne.

Ponieważ IComparer jest tylko jednorazowym obiektem używanym do sortowania w tym momencie, twój obiekt może mieć dowolną zmienną semantykę, którą chcesz. Co więcej, nie wymaga, ani nawet nie sugeruje używania Equals, GetHashCode ani == - możesz to zdefiniować w dowolny sposób.

Na koniec możesz zdefiniować wiele IComparer dla swojego typu w celu sortowania według różnych pól lub według różnych reguł. Jest to o wiele bardziej elastyczne niż utknięcie z jedną definicją.

W skrócie: użyj IComparable dla typów wartości i IComparer dla typów referencyjnych.

Panie Bungle
źródło
6

Proste wyjaśnienie poprzez opowieść

Koszykówka w liceum. To szkolny wybór dla drużyn. Chcę mieć najwyższych / najlepszych / najszybszych ludzi w moim zespole. Co ja robię?

Interfejs iComparer - porównaj dwie osoby oddzielne osoby

  • To pozwala mi porównać dowolnych dwóch ustawionych w kolejce facetów… to w zasadzie wszystko. Fred vs John .......... wrzucam je do konkretnej klasy, która implementuje interfejs. Compare(Fred, John)i wypluwa, kto jest lepszy.

A co z IComparable? - Porównaj się z kimś innym

Byłeś ostatnio na FB? Widzisz innych ludzi robiących fajne rzeczy: podróżowanie po świecie, tworzenie wynalazków, podczas gdy ja robię coś niezbyt fajnego - cóż, to, co robimy, to korzystanie z interfejsu IComparable.

  • Porównujemy bieżącą instancję (siebie) z innym obiektem (kimś innym), który jest tego samego typu (osoba).

A co z klasą porównującą?

Klasa Comparer jest abstrakcyjną klasą bazową, która implementuje interfejs IComparer. Powinieneś wywodzić się z tej klasy, aby mieć konkretną implementację. w każdym razie firma Microsoft zaleca, aby zamiast implementować interfejs IComparer, UŻYWASZ klasy Comparer:

Zalecamy wyprowadzenie z klasy Comparer zamiast implementowania interfejsu IComparer, ponieważ klasa Comparer zapewnia jawną implementację interfejsu metody IComparer.Compare i właściwość Default, która pobiera domyślną funkcję porównującą dla obiektu.

Podsumowanie

  • IComparer - wyrównaj dwie rzeczy i porównaj.
  • IComparable - porównaj się z innymi na FB.

Mam nadzieję, że historie pomogą Ci zapamiętać.

BKSpurgeon
źródło
1
Podoba mi się twój sposób na zilustrowanie kluczowej koncepcji. Byłoby lepiej, gdybyś włączył do tego konkursu klasę Comparer (T). Nawet tego nie ma w pytaniu. :)
Kevman
4

Jak powiedzieli inni, nie robią tego samego.

W każdym razie, obecnie nie używam IComparer. Dlaczego miałabym? Jego odpowiedzialność (zewnętrzna jednostka używana do porównywania dwóch obiektów) może być obsługiwana znacznie lepiej za pomocą wyrażenia lambda, podobnie jak działa większość metod LINQ. Napisz szybką lambdę, która pobiera obiekty do porównania jako argumenty i zwraca wartość bool. A jeśli obiekt definiuje własną wewnętrzną operację porównania, może zamiast tego zaimplementować IComparable.

jalf
źródło
1
-1: zwrócenie bool nie jest równoważne z IComparer. IComparer zwraca wartość, która może być mniejsza od zera / zero / większa od zera i jest zwykle używana do sortowania.
Joe
A kiedy tego potrzebujesz, zamiast tego zwracasz int (lub lepiej wyliczenie). Czy to naprawdę wielka sprawa?
jalf
2
Możesz nawet zwrócić wartość bool, ponieważ mniej niż jest jedyną operacją potrzebną do posortowania sekwencji.
jalf
implementacja IComparer musi być zdefiniowana tylko raz, jeśli musisz użyć logiki sortowania w większej liczbie miejsc, to wyrażenie lambda będzie musiało zostać zapisane więcej razy.
oɔɯǝɹ
oɔɯǝɹ - Następnie możesz zapisać odwołanie do delegata, które zostało zapisane jako wyrażenie lambda, i użyć go ponownie.
jpierson,
3

IComparable mówi, że obiekt można porównać z innym. IComparer to obiekt, który może porównać dowolne dwa elementy.

Neil Barnwell
źródło
2

IComparer jest interfejsem używanym do sortowania tablicy, ten interfejs wymusi na klasie implementację metody Compare (T x, T y), która porówna dwa obiekty. Instancja klasy, która zaimplementowała ten interfejs, jest używana do sortowania tablicy Array.

IComparable to interfejs jest zaimplementowany w typie, który musi porównać dwa obiekty tego samego typu, Ten porównywalny interfejs wymusi na klasie implementację następującej metody CompareTo (T obj)

IEqualityComparer to interfejs, który służy do znajdowania obiektu, niezależnie od tego, czy jest równy, czy nie. Teraz zobaczymy to na przykładzie, w którym musimy znaleźć Distinct obiektu w kolekcji. Ten interfejs zaimplementuje metodę Equals (T obj1, T obj2)

Teraz bierzemy przykład, mamy klasę Employee, na podstawie tej klasy musimy stworzyć kolekcję. Teraz mamy następujące wymagania.

Sortuj tablicę za pomocą klasy Array 2. Potrzebujesz kolekcji za pomocą Linq: usuń duplikat, uporządkuj od wyższego do niższego, usuń jeden identyfikator pracownika

abstract public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Address { set; get; }
}

public enum SortType
{
    ByID,
    BySalary
}

public class EmployeeIdSorter: IComparer {public int Compare (Employee x, Employee y) {if (x.Id <y.Id) return 1; else if (x.Id> y.Id) return -1; else return 0; }}

    public class EmployeeSalarySorter : IComparer<Employee>
    {
        public int Compare(Employee x, Employee y)
        {
            if (x.Salary < y.Salary)
                return 1;
            else if (x.Salary > y.Salary)
                return -1;
            else
                return 0;
        }
    }

Więcej informacji można znaleźć poniżej http://dotnetvisio.blogspot.in/2015/12/usage-of-icomparer-icomparable-and.html

Rajesh G
źródło