默认比较器如何在C#中工作?

时间:2015-08-11 22:10:35

标签: c# linq sorting comparator

我正在使用OrderBy进行基于属性的排序,我找到了documentation for the default comparer,但这并没有给我解释太多。如果某个对象未实现System.IComparable<T>,它如何生成Comparer<T>

例如,我目前正在根据类型object的属性值对对象列表进行排序。它们是下面的数字类型,排序工作正常。 C#/ Linq如何知道如何对对象进行排序?它是否会对基元进行一些解除武装?它是否进行了一些哈希检查?怎么会转化为大于或小于?

如果它们是一个更复杂的类型,它会因错误而失败,或者OrderBy什么都不做,或者它甚至会以一种没有意义的方式排序?

4 个答案:

答案 0 :(得分:2)

您可以查看参考来源和see for yourself它的作用。

    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

Here is the Compare code

    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"));
    }

正如您所看到的那样,它检查ab是否实现了IComparable,如果它们都没有引发异常。

答案 1 :(得分:1)

浏览Reference Source时,会返回ObjectComparer<T>,这是一种特殊的内部类型,只会将工作委托给System.Collections.Comparer.Default

如果接收到未实现IComparable的参数,则会抛出异常。由于该比较器通过向下转换和反射工作,因此它不关心对象的静态类型是否未实现IComparable(如果您有object的列表,则会出现这种情况。) p>

所以底线是这样的:首先检查IComparable<T>,然后检查IComparable,最后抛出异常。

顺便说一下,大多数(我会说都是)内置类型以某种方式实现IComparable<T>,这就是它们的排序方式。

答案 2 :(得分:0)

int,或者更确切地说,Int32 确实实现IComparable,因此它可以正常运行。 (source

OrderBy似乎尝试将比较器用于它遇到的第一种类型,因此如果您从一个未实现IComparable的对象开始,您将获得ArgumentException

  

至少有一个对象必须实现IComparable

如果您首先说Int32,那么您将获得相同的例外:

  

对象必须是Int32

类型

来自Int32

的比较器

答案 3 :(得分:0)

查看内部,如果对象是通用的并且实现IComparable<T>,则默认比较器将返回一个GenericComparer实例,该实例将对象强制转换为该接口以执行比较。原始类型已经实现了它。 Nullable基元类型也自动实现该接口,因此返回的NullableComparer的工作方式类似。在这些情况下没有装箱/拆箱。

否则,它会尝试将对象强制转换为非泛型IComparable实例,这些实例可能导致使用结构进行装箱,或者如果类型没有实现,则抛出ArgumentException