IComparer не используется в выражениях LINQ - PullRequest
0 голосов
/ 19 апреля 2020

Я вижу странное поведение и не могу понять, как это может произойти. У меня есть 2 отсортированных набора, которые я использую буферы для переупорядочения пакетов TCP по порядковому номеру, и я читаю пакеты из этих наборов в выражении блокировки.

Вот упрощенный пример:

SortedSet<Packet> in = new SortedSet<TcpPacket>(new SeqComparer()); // inbound buffer
SortedSet<Packet> out = new SortedSet<TcpPacket>(new SeqComparer()); // outbound buffer

public void BufferPacket(Packet packet) {
    // Some code which does not modify buffers
    bool buffer = isOutbound ? out : in;
    lock (buffer) {
        buffer.Add(packet);

        Packet bufferedPacket;
        // Removing retransmitted packets
        while (buffer.Count > 0 && (bufferedPacket = buffer.Min()).SeqNum < expected) {
            buffer.Remove(bufferedPacket);
        }

        // Process buffer
        while (buffer.Count > 0 && (bufferedPacket = buffer.Min()).SeqNum == expected) {
            buffer.Remove(bufferedPacket);
            expected += bufferedPacket.Length;
            ...
        }
    }
}

И что-то вызывает это исключение:

System.ArgumentException: At least one object must implement IComparable.
   at System.Collections.Comparer.Compare(Object a, Object b)
   at System.Linq.Enumerable.Min[TSource](IEnumerable`1 source)

, что, по-видимому, означает, что после проверки Count > 0, Min() не обнаружил никаких объектов. Я очень озадачен тем, как это может произойти, когда вы заперты.

Ответы [ 2 ]

1 голос
/ 19 апреля 2020

Это ответ, извлеченный из комментариев под вопросом

Это , а не проблема, связанная с блокировкой.

Согласно комментариям, OP реализовано IComparer<Packet> и передано в SortedSet<Packet>. Сравнитель, хотя он используется коллекцией, метод Min, являющийся расширением LINQ, не использует его.

Вместо этого Min ожидает, что класс Packet реализует IComparable. И поскольку класс, похоже, не реализовал интерфейс, возникает исключение.

Min не сравнивает объекты, когда коллекция содержит только 0 или 1 элементы. Когда коллекция содержит 2 или более элементов, Min сравнивает Packet и выдается исключение.

Согласно комментарию ФП, вот где путаница была:

Большую часть времени в буфере находится только 1 пакет (за исключением редких случаев неупорядоченных пакетов, которые я пытаюсь решить с этим буфером.)

Однако сообщение об ошибке в этом сценарии также может вводить в заблуждение:

Как минимум один объект должен реализовывать IComparable.

Это означает, что в наборе может не быть элементов.

Чтобы устранить эту проблему, поскольку наборы уже упорядочены, следует использовать First или Last в соответствии с реализацией IComparer<Packet>.

0 голосов
/ 19 апреля 2020

Вместо использования метода расширения Enumerable.Min библиотеки LINQ используйте вместо него свойство SortedSet.Min:

Получает минимальное значение в SortedSet<T>, как определено компаратором.

...