Преимущества/Недостатки различных реализаций для Сравнения Объектов

Это подвергает сомнению, включает 2 различных реализации по существу того же кода.

Во-первых, с помощью делегата для создания Метода сравнения, который может использоваться в качестве параметра при сортировке набора объектов:

class Foo
{
    public static Comparison<Foo> BarComparison = delegate(Foo foo1, Foo foo2)
    {
        return foo1.Bar.CompareTo(foo2.Bar);
    };
}

Я использую вышеупомянутое, когда я хочу иметь способ отсортировать набор объектов Foo по-другому, чем мои предложения функции CompareTo. Например:

List<Foo> fooList = new List<Foo>();
fooList.Sort(BarComparison);

Во-вторых, использование IComparer:

public class BarComparer : IComparer<Foo>
{
    public int Compare(Foo foo1, Foo foo2)
    {
        return foo1.Bar.CompareTo(foo2.Bar);
    }
}

Я использую вышеупомянутое, когда я хочу сделать двоичный поиск объекта Foo в наборе объектов Foo. Например:

BarComparer comparer = new BarComparer();
List<Foo> fooList = new List<Foo>();
Foo foo = new Foo();
int index = fooList.BinarySearch(foo, comparer);

Мои вопросы:

  • Каковы преимущества и недостатки каждой из этих реализаций?
  • Что такое еще некоторые способы использовать в своих интересах каждую из этих реализаций?
  • Существует ли способ объединить эти реализации таким способом, которым я не должен копировать код?
  • Я могу достигнуть и двоичного поиска и альтернативного вида набора с помощью только 1 из этих реализаций?
18
задан Bakuriu 28 April 2014 в 16:37
поделиться

5 ответов

Вероятно, самым большим преимуществом принятия Comparison по сравнению с IComparer является возможность писать анонимные методы. Если у меня есть, скажем, список List , где MyClass содержит свойство ID , которое должно использоваться для сортировки, я могу написать:

myList.Sort((c1, c2) => c1.ID.CompareTo(c2.ID));

Что намного удобнее, чем писать полную реализацию IComparer .

Я не уверен, что принятие IComparer действительно имеет какие-либо серьезные преимущества, за исключением совместимости с устаревшим кодом (включая классы .NET Framework). Свойство Comparer .Default действительно полезно только для примитивных типов; все остальное обычно требует дополнительной работы для кодирования.

Чтобы избежать дублирования кода, когда мне нужно работать с IComparer , обычно я создаю общий компаратор, например:

public class AnonymousComparer<T> : IComparer<T>
{
    private Comparison<T> comparison;

    public AnonymousComparer(Comparison<T> comparison)
    {
        if (comparison == null)
            throw new ArgumentNullException("comparison");
        this.comparison = comparison;
    }

    public int Compare(T x, T y)
    {
        return comparison(x, y);
    }
}

Это позволяет писать такой код, как:

myList.BinarySearch(item,
    new AnonymousComparer<MyClass>(x.ID.CompareTo(y.ID)));

Это не совсем красиво, но экономит время.

Еще один полезный класс, который у меня есть, - это:

public class PropertyComparer<T, TProp> : IComparer<T>
    where TProp : IComparable
{
    private Func<T, TProp> func;

    public PropertyComparer(Func<T, TProp> func)
    {
        if (func == null)
            throw new ArgumentNullException("func");
        this.func = func;
    }

    public int Compare(T x, T y)
    {
        TProp px = func(x);
        TProp py = func(y);
        return px.CompareTo(py);
    }
}

Который вы можете написать код, предназначенный для IComparer , как:

myList.BinarySearch(item, new PropertyComparer<MyClass, int>(c => c.ID));
6
ответ дан 30 November 2019 в 09:21
поделиться

Они действительно удовлетворяют разные потребности:

IComparable полезен для упорядоченных объектов. Действительные числа должны быть сопоставимы, но комплексные числа не могут - это плохо определено.

IComparer позволяет определять многоразовые, хорошо инкапсулированные компараторы. Это особенно полезно, если для сравнения требуется дополнительная информация. Например, вы можете сравнить дату и время из разных часовых поясов. Это может быть сложно, и для этой цели следует использовать отдельный компаратор.

Метод сравнения предназначен для простых операций сравнения, которые недостаточно сложны для того, чтобы возможность повторного использования представляла собой какую-либо проблему, например сортировка списка клиентов по имени. Это простая операция, поэтому дополнительные данные не требуются. Точно так же это не присуще объекту, потому что объекты никаким образом не упорядочены естественным образом.

Наконец, существует IEquatable , что может быть важно, если ваш метод Equals может только определять, равны ли два объекта или нет, но если нет понятия «больше» и "меньше", например комплексные числа или векторы в пространстве.

0
ответ дан 30 November 2019 в 09:21
поделиться

Нет никаких преимуществ ни у одного из вариантов с точки зрения производительности. Это действительно вопрос удобства и ремонтопригодности кода. Выберите тот вариант, который вам больше нравится. При этом рассматриваемые методы немного ограничивают ваш выбор.

Вы можете использовать интерфейс IComparer для List .Sort , что позволит вам не дублировать код.

К сожалению, BinarySearch не реализует параметр, использующий Comparison , поэтому вы не можете использовать делегат Comparison для этого метода ( по крайней мере, не напрямую).

Если вы действительно хотите использовать Comparison для обоих, вы можете создать общую реализацию IComparer , которая принимает Comparison в своем конструкторе и реализовал IComparer .

public class ComparisonComparer<T> : IComparer<T>
{
    private Comparison<T> method;
    public ComparisonComparer(Comparison<T> comparison)
    {
       this.method = comparison;
    }

    public int Compare(T arg1, T arg2)
    {
        return method(arg1, arg2);
    }
}
7
ответ дан 30 November 2019 в 09:21
поделиться

Техника делегирования очень короткая (лямбда-выражения могут быть еще короче), поэтому, если ваша цель - более короткий код, то это преимущество.

Однако реализация IComparer (и его универсального эквивалента) делает ваш код более тестируемым: вы можете добавить некоторое модульное тестирование к вашему классу / методу сравнения.

Кроме того, вы можете повторно использовать свою реализацию компаратора при составлении двух или более компараторов и объединении их в качестве нового компаратора. Повторное использование кода с анонимными делегатами труднее.

Итак, подведем итог:

Анонимные делегаты : более короткий (и, возможно, более чистый) код

Явная реализация : возможность тестирования и повторное использование кода.

1
ответ дан 30 November 2019 в 09:21
поделиться

В вашем случае преимущество наличия делегата IComparer перед Comparision в том, что вы также можете использовать его для метода Sort, поэтому не Мне вообще не нужна версия делегата Comparison .

Еще одна полезная вещь, которую вы можете сделать, - это реализовать делегированную реализацию IComparer следующим образом:

public class DelegatedComparer<T> : IComparer<T>
{
  Func<T,T,int> _comparision;
  public DelegatedComparer(Func<T,T,int> comparision)
  {
    _comparision = comparision;
  }
  public int Compare(T a,T b) { return _comparision(a,b); }
}

list.Sort(new DelegatedComparer<Foo>((foo1,foo2)=>foo1.Bar.CompareTo(foo2.Bar));

и более продвинутую версию:

public class PropertyDelegatorComparer<TSource,TProjected> : DelegatedComparer<TSource>
{
  PropertyDelegatorComparer(Func<TSource,TProjected> projection)
    : base((a,b)=>projection(a).CompareTo(projection(b)))
}
0
ответ дан 30 November 2019 в 09:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: