How does the default comparator work in C#?
I'm using OrderBy for some sorting based on properties and I found the documentation for the default comparer but it didn't explain much to me. If an object doesn't implement System.IComparable<T>
, how does it generate a Comparer<T>
?
For instance, I'm currently sorting a list of objects based a property value of type object
. They are numeric types underneath and the sorting works fine. How does C#/Linq know how to sort the object? Does it do some un-boxing into primitives? Does it do some hash checking? How would that translate into greater than or less than?
If they were a more complex type would this fail with an error or would OrderBy do nothing, or would it even sort in a way that made no sense?
Well you can check the reference source and see for yourself what it does.
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>();
}
So what it does is it checks if the type implements IComparable<T>
, if it does it uses the comparer built in to the type (your list of objects that are numeric types would follow this branch). It then does the same check again in case the type is a Nullable<ICompareable<T>>
. If that also fails it uses the ObjectComparer
which uses Comparer.Default
.
Here is the Compare code for 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"));
}
As you can see it checks if a
or b
implements IComparable
and if neither do it throws a exception.
Browsing on Reference Source, it returns an ObjectComparer<T>
, which is a special internal type that just delegates the work to System.Collections.Comparer.Default
.
This, in turn, throws an exception if it receives parameters that do not implement IComparable
. Since that comparer works through downcasting and reflection, then it does not care if the static type of the object does not implement IComparable
(which is the case if you have a list of object
s).
So the bottom line is this: first it checks for IComparable<T>
, then it checks for IComparable
, and finally it throws an exception.
By the way, most (I'd say all even) built-in types implement IComparable<T>
in some way, so that's how they can be sorted.
int
, or to be more precise, Int32
does actually implement IComparable
, so it works. (source)
OrderBy
appears to attempt to use the comparator for the first type it comes across, so if you start with an object that doesn't implement IComparable
, you will get an ArgumentException
:
At least one object must implement IComparable
If you start with say, an Int32
, then you will get the same exception with:
Object must be of type Int32
From the comparer for Int32
上一篇: pip install numpy不起作用:“找不到匹配的分配”
下一篇: 默认比较器如何在C#中工作?