Почему C # позволяет мне компилировать код сортировки, когда он не знает, как сортировать - PullRequest
2 голосов
/ 11 марта 2009

Мне показалось странным, что C # позволил мне вызывать sort в моем классе, не указывать способ их сортировки или писать перегрузки сравнения. Когда я запустил этот код, эта ошибка высветилась

List<MyClass> myClassArray= new List<MyClass>();
//myClassArray.add(...);
myClassArray.Sort();

An unhandled exception of type 'System.InvalidOperationException' occurred in mscorlib.dll

Additional information: Failed to compare two elements in the array.

Почему C # позволяет мне компилировать этот код, когда он не знает, как это отсортировать! -edit-

Кодекс, спроси, почему он это делает. Я написал теорию о том, почему это происходит в моих комментариях. Вот пример кода.

class A : IComparable<A>
{
    public int CompareTo(A a) { return 0; }
}
class C //: IComparable<A>
{
    public int CompareTo(A a) { return 0; }
}
    static void test()
    {
        A a = new A();
        bool b;
        C c = new C();

        object o = a;
        IComparable<A> ia = (IComparable<A>)o;
        b = ia == ia;

        o = c;
        IComparable<A> ic = (IComparable<A>)o;
        b = ic == ic;

        //uncomment this to get a compile time error
        //IComparable<A> ic2 = c;
        return;
    }

Если вы раскомментируете строку перед возвратом, вы получите ошибку времени компиляции. Когда вы раскомментируете IComparable в классе c, он будет компилироваться и работать.

Ответы [ 4 ]

13 голосов
/ 11 марта 2009

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

2 голосов
/ 11 марта 2009

Просто для дополнительной информации; по существу, он использует Comparer<T>.Default для сравнения. Это реализует IComparer<T> и может сравнивать 2 объекта типа T. Фактическая реализация выбирается при первом запросе (за T); Есть ряд шаблонов, которые фреймворк использует для выбора реализации - например, классы, «обычные» структуры и Nullable<T> все обрабатываются отдельно. Кроме того, он делает выбор в зависимости от того, реализует ли T IComparable<T>, IComparable или ни того, ни другого (в этом случае он генерирует исключение).

Это обеспечивает очень простой способ сортировки по типу утки. Аналогично, есть также EqualityComparer<T>.Default, который проверяет IEquatable<T>, по умолчанию object.Equals в противном случае.

2 голосов
/ 11 марта 2009

Сортировка должна проверять, реализует ли ваш объект IComparable . Это проверка во время выполнения, и, поскольку вы, по-видимому, не реализовали ее, компаратор по умолчанию не знает, что делать с вашими объектами, поэтому выдает исключение.

Позволяет компилировать, потому что это не языковая функция, а фреймворк.

0 голосов
/ 27 января 2010

C #, возможно, мог просто запутать представление в System.Object о том, как вы упорядочиваете объекты, как это было сделано с Equals, чтобы сравнить их на предмет идентичности.

К сожалению, это приводит к проблемам интенсиональности и экстенсиональности, локализации и т. Д.

Существует интерфейс IComparable<T>, но встроенные типы значений не могут реализовывать подобные интерфейсы.

Следовательно, нет хорошего способа просто посмотреть на тип во время компиляции и точно знать, имеет ли он осмысленный порядок. = (

Механизм, который развился в C #, состоит в том, чтобы использовать экземпляр IComparer<T>, возвращаемый Comparer<T>.Default, и получать ошибку во время выполнения при попытке отсортировать что-либо, в котором отсутствует упорядоченность.

Допуская несколько IComparer с и IComparer<T> с, вы можете иметь представление о нескольких альтернативных упорядочениях, которые работают с одним и тем же типом, так что многое хорошо, но не все хорошо.

Внутренне, c # использует набор правил для поиска Comparer<T>.Default, который обрабатывается по-разному, если T является экземпляром IComparable<T> или имеет форму Nullable<T> и т. Д.

например. код из system / collection / generic / comparer.cs :

    public static Comparer<T> Default {
        get {
            Comparer<T> comparer = defaultComparer;
            if (comparer == null) {
                comparer = CreateComparer();
                defaultComparer = comparer;
            }
            return comparer;
        }
    }

    private static Comparer<T> CreateComparer() {
        Type t = typeof(T);
        // If T implements IComparable<T> return a GenericComparer<T>
        if (typeof(IComparable<T>).IsAssignableFrom(t)) {
            //return (Comparer<T>)Activator.CreateInstance(typeof(GenericComparer<>).MakeGenericType(t));
            return (Comparer<T>)(typeof(GenericComparer<int>).TypeHandle.CreateInstanceForAnotherGenericParameter(t));
        }
        // If T is a Nullable<U> where U implements IComparable<U> return a NullableComparer<U>
        if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) {
            Type u = t.GetGenericArguments()[0];
            if (typeof(IComparable<>).MakeGenericType(u).IsAssignableFrom(u)) {
                //return (Comparer<T>)Activator.CreateInstance(typeof(NullableComparer<>).MakeGenericType(u));
                return (Comparer<T>)(typeof(NullableComparer<int>).TypeHandle.CreateInstanceForAnotherGenericParameter(u));                
            }
        }
        // Otherwise return an ObjectComparer<T>
        return new ObjectComparer<T>();
    }

По сути, это позволяет Microsoft постепенно добавлять специальные случаи для своего собственного кода, но, к сожалению, код, который получается в результате использования Comparer<T>.Default для пользовательского IComparable<T> экземпляра, довольно ужасен.

Метод Sort в IList<T> предполагает, что Comparer<T>.Default найдет что-то, что он может использовать для сравнения ваших объектов, но у него нет способа посмотреть на тип T и сказать, что он есть, поэтому он предполагает является безопасным и осознает только позже во время выполнения, что MyClass не подыгрывает.

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