Как компаратор по умолчанию работает в C#?

Я использую OrderBy для некоторой сортировки на основе свойств, и я нашел документация для компаратора по умолчаниюSystem.IComparable<T>, как он генерирует Comparer<T>?

например, в настоящее время я сортирую список объектов на основе значения свойства типа object. Они являются числовыми типами внизу, и сортировка работает нормально. Как C# / Linq знает, как сортировать объект? Делает ли это un-boxing в примитивных? Он проверяет хэш? Как это можно перевести в большее или меньшее?

4 ответов


Ну вы можете проверить источник ссылки и смотрите сами что она делает.

    public static Comparer<T> Default {
        get {
            Contract.Ensures(Contract.Result<Comparer<T>>() != null);

            Comparer<T> comparer = defaultComparer;
            if (comparer == null) {
                comparer = CreateComparer();
                defaultComparer = comparer;
            }
            return comparer;
        }
    }
    private static Comparer<T> CreateComparer() {
        RuntimeType t = (RuntimeType)typeof(T);

        // If T implements IComparable<T> return a GenericComparer<T>
#if FEATURE_LEGACYNETCF
        //(SNITP)
#endif
            if (typeof(IComparable<T>).IsAssignableFrom(t)) {
                return (Comparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(GenericComparer<int>), t);
            }

        // If T is a Nullable<U> where U implements IComparable<U> return a NullableComparer<U>
        if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) {
            RuntimeType u = (RuntimeType)t.GetGenericArguments()[0];
            if (typeof(IComparable<>).MakeGenericType(u).IsAssignableFrom(u)) {
                return (Comparer<T>)RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType)typeof(NullableComparer<int>), u);
            }
        }
        // Otherwise return an ObjectComparer<T>
      return new ObjectComparer<T>();
    }

Итак, что он делает, это проверяет, реализует ли тип IComparable<T>, если это так, он использует компаратор, встроенный в тип (ваш список объектов, которые являются числовыми типами, будет следовать этой ветви). Затем он снова выполняет ту же проверку, если тип является Nullable<ICompareable<T>>. Если это также не удается, он использует ObjectComparer использует Comparer.Default.

вот сравнение код на Comparer.Default

    public int Compare(Object a, Object b) {
        if (a == b) return 0;
        if (a == null) return -1;
        if (b == null) return 1;
        if (m_compareInfo != null) {
            String sa = a as String;
            String sb = b as String;
            if (sa != null && sb != null)
                return m_compareInfo.Compare(sa, sb);
        }

        IComparable ia = a as IComparable;
        if (ia != null)
            return ia.CompareTo(b);

        IComparable ib = b as IComparable;
        if (ib != null)
            return -ib.CompareTo(a);

        throw new ArgumentException(Environment.GetResourceString("Argument_ImplementIComparable"));
    }

как вы можете видеть, он проверяет, если a или b осуществляет IComparable и если ни то, ни другое не вызывает исключения.


просмотр Источник, он возвращает ObjectComparer<T>, который является специальным внутренним типом, который просто делегирует работу System.Collections.Comparer.Default.

это, в свою очередь, бросает исключение, если он получает параметры, которые не реализуют IComparable. Поскольку этот компаратор работает через downcasting и отражение, то ему все равно, если статический тип объекта не реализует IComparable (который имеет место, если у вас есть список objects).

Итак строка такова: сначала она проверяет IComparable<T>, затем он проверяет IComparable, и, наконец, он выдает исключение.

кстати, большинство (я бы сказал, все даже) встроенных типов реализуют IComparable<T> в некотором роде, так вот как их можно сортировать.


int, а если быть более точным, Int32 тут на самом деле реализовать IComparable, Так что это работает. (источник)

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

по крайней мере один объект должен реализовать IComparable

если вы начинаете с say, an Int32, тогда вы получите то же исключение с:

объект должен иметь тип Int32

от компаратора для Int32


глядя на внутренние, если объект является общим и реализует IComparable<T>, компаратор по умолчанию вернет GenericComparer экземпляр, который приводит объекты к этому интерфейсу для выполнения сравнений. Примитивные типы уже реализуют его. Nullable примитивные типы автоматически реализуют этот интерфейс, а также, поэтому возвращаемый NullableComparer работает аналогично. В этих сценариях нет бокса/распаковки.

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