C # BinarySearch разрывается при наследовании от чего-то, что реализует IComparable <T>? - PullRequest
5 голосов
/ 02 мая 2010

В .NET алгоритм BinarySearch (в списках, массивах и т. Д.), По-видимому, не работает, если элементы, которые вы пытаетесь найти, наследуют от IComparable вместо прямой реализации:

List<B> foo = new List<B>(); // B inherits from A, which implements IComparable<A>
foo.Add(new B());
foo.BinarySearch(new B());   // InvalidOperationException, "Failed to compare two elements in the array."

Где:

public abstract class A : IComparable<A>
{
    public int x;

    public int CompareTo(A other)
    {
        return x.CompareTo(other.x);
    }
}

public class B : A {}

Есть ли способ обойти это? Реализация CompareTo (B other) в классе B, похоже, не работает.

Ответы [ 3 ]

7 голосов
/ 02 мая 2010

Документация проясняет это:

проверяет, реализует ли тип T универсальный интерфейс IComparable, и использует эту реализацию, если она доступна. Если нет, Comparer.Default проверяет, реализует ли тип T интерфейс IComparable. Если тип T не реализует какой-либо интерфейс, Comparer.Default генерирует исключение InvalidOperationException.

Итак, простое решение - реализовать неуниверсальный интерфейс IComparable.
Добавление CompareTo(B other) будет работать для вас, если вы также реализуете IComparable<B> - вы, вероятно, забыли этот бит.

Интересным решением является компиляция кода с использованием C # 4, где он работает без ошибок. В C # 4 вводится общая ковариация: public interface IComparable<in T> против public interface IComparable<T>, и опубликованный код работает, как и ожидалось.

1 голос
/ 02 мая 2010

Правильно, поэтому проблема в том, что он пытается увидеть, реализует ли class B IComparable<B>, чего он не делает, потому что на самом деле реализует IComparable<A>, затем пытается IComparable, а затем сдается.Как указал Коби, реализация неуниверсального IComparable решит эту проблему.

0 голосов
/ 14 октября 2011

Хотя приведенные выше ответы верны для версий .net до 4.0, стоит отметить, что .Net 4.0 определяет IComparable ковариантно, что означает, что если класс реализует IComparable , но не реализует явно IComparable реализация, если IComparable будет рассматриваться как реализация, если IComparable .

Обратите внимание, что это не относится к IEquatable , но поскольку производные типы не должны реализовывать IEquatable , это не должно быть проблемой.

Обратите внимание, что в отличие от IEquatable , который логически связан с реализацией семантики, очень похожей на Object.Equals (поскольку объекты, реализации GetHashCode возвращающие неравное, должны сравнивать неравное), существует ряд случаев, когда IComparable должен логически return равно, хотя IComparable должен сравнивать неравное (в таких случаях производный тип должен обычно реализовывать IComparable , но не переопределять IComparable . Обратите внимание, что если IComparable .Compare возвращает ноль, это не означает, что что объекты равны , но просто то, что ни один из них не стоит окончательно над другим.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...