Как компаратор по умолчанию работает в 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
(который имеет место, если у вас есть список object
s).
Итак строка такова: сначала она проверяет IComparable<T>
, затем он проверяет IComparable
, и, наконец, он выдает исключение.
кстати, большинство (я бы сказал, все даже) встроенных типов реализуют IComparable<T>
в некотором роде, так вот как их можно сортировать.
int
, а если быть более точным, Int32
тут на самом деле реализовать IComparable
, Так что это работает. (источник)
OrderBy
похоже, пытается использовать компаратор для первого типа, с которым он сталкивается, поэтому, если вы начинаете с объекта, который не реализует IComparable
, вы ArgumentException
:
по крайней мере один объект должен реализовать IComparable
если вы начинаете с say, an Int32
, тогда вы получите то же исключение с:
объект должен иметь тип
Int32
от компаратора для Int32
глядя на внутренние, если объект является общим и реализует IComparable<T>
, компаратор по умолчанию вернет GenericComparer
экземпляр, который приводит объекты к этому интерфейсу для выполнения сравнений. Примитивные типы уже реализуют его. Nullable примитивные типы автоматически реализуют этот интерфейс, а также, поэтому возвращаемый NullableComparer
работает аналогично. В этих сценариях нет бокса/распаковки.
в противном случае он попытается бросить объекты в неродовые IComparable
экземпляры, которые может вызвать бокс со структурами или бросить ArgumentException
если тип не реализует его.