Как сравнивать кортежи с помощью пользовательского компаратора для записей, но компаратора по умолчанию для кортежа?

Я хочу отсортировать List<Tuple<Vertex, Vertex>>, то есть список кортежей, где каждый кортеж содержит определенное количество вершин.
Vertex — это пользовательский класс, List и Tuple — из System.

У меня уже есть несколько Comparer, которые позволяют сравнивать две вершины, например:
class MyVertexComparer1 : Comparer<Vertex> и class MyVertexComparer2 : Comparer<Vertex>

Теперь я хотел бы использовать эти существующие Comparer для сортировки списка в соответствии со сравнением кортежей по умолчанию, т.е. сравнением первой записи и только в случае ничьей сравнения следующей записи.
Сравнение двух кортежей в рамках этой сортировки должно определяться одним из пользовательских VertexComparers.

Я знаю, что мог бы написать class MyTupleComparer : Comparer<Tuple<Vertex, Vertex>>, который использует MyVertexComparer в своей реализации, возможно, с общим параметром, указывающим, какой VertexComparer использовать. Однако это кажется неправильным, поскольку я просто повторил бы сравнение по умолчанию для кортежей.
Более того, я не понимаю, как это можно распространить на кортежи с более чем двумя вершинами без специального класса компаратора для каждого количества вершин.


person Aaron    schedule 25.08.2020    source источник
comment
Не могли бы вы уточнить использование компараторов по умолчанию и пользовательских компараторов во время сортировки? Похоже, что компаратор по умолчанию делает то, что вы хотите, исходя из моего понимания того, что вы просите. Если вы назовете Sort() в своем списке, что не так с результирующим порядком?   -  person CoolBots    schedule 25.08.2020
comment
@CoolBots Я обновил вопрос, надеюсь, он разъясняет использование пользовательских компараторов и компараторов по умолчанию.   -  person Aaron    schedule 25.08.2020


Ответы (2)


Сделайте Vertex IComparable<Vertex>, и Sort по умолчанию на List<T> будет работать так, как вы описываете; то есть для Tuple будет использоваться компаратор по умолчанию, поскольку пользовательский компаратор не предоставляется, а для записей будет использоваться метод Vertex.CompareTo.

Если вы хотите повторно использовать существующие Comparer, вы можете делегировать/совместно использовать функциональность с реализацией IComparable<Vertex>.CompareTo; но вы не можете делать то, что хотите, либо не написав еще один Comparer для Tuple (это будет для каждого типа Tuple, поскольку Tuple<T1, T2> - это другой тип, чем Tuple<T1, T2, T3>), либо внедрив IComparable<Vertex> в тип Vertex .

person CoolBots    schedule 25.08.2020
comment
Спасибо за ответ. Правильно ли я понимаю, что преобразование Vertex в IComparable определило бы один вид сравнения вершин? Если нет, как я могу определить несколько способов сравнения вершин с подходом IComparable<Vertex>? - person Aaron; 25.08.2020
comment
Это фактически определило бы метод точки входа по умолчанию для сравнения Vertex типа; внутри этого метода вы все равно можете написать логику для делегирования сравнения другим методам по мере необходимости. Например, ваш тип Vertex может иметь конструктор, который принимает Comparer, или массив Comparer, которые можно использовать в методе CompareTo, в зависимости от некоторой логики, значений и т. д. - person CoolBots; 25.08.2020
comment
Хорошо, думаю, я буду придерживаться Comparer для Tuple, чтобы варианты сортировки списка были отделены от класса данных Vertex. Тем не менее приятно знать, что нет возможности повторно использовать сравнения по умолчанию таким образом. - person Aaron; 25.08.2020

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

class ComparePair<TComp> : Comparer<Tuple<Vertex, Vertex>> where TComp : Comparer<Vertex>, new() {
    private readonly TComp comp = new TComp();

    public override int Compare(Tuple<Vertex, Vertex> a, Tuple<Vertex, Vertex> b) {
        int res = comp.Compare(a.Item1, b.Item1);
        return res == 0 ? comp.Compare(a.Item2, b.Item2) : res;
    }
}
person Aaron    schedule 25.08.2020