首页 > 学院 > 开发设计 > 正文

.NetFramework4.0内部排序探索

2019-11-14 13:48:31
字体:
来源:转载
供稿:网友

简介

一时好奇心起,想一窥.Net Framework 4.0内部究竟是使用何种算法排序。以前听人说Framework内部是使用的快速排序,但究竟耳听为虚,眼见为实。主要通过JetBrains dotPeek 1.2作为工具反编译Framework的代码进行查看,也参考了其他很多资料。本人才疏学浅,其中难免存在错误,希望大家不吝指教。

数组

众所周知,数组实质上是Array类的实例。呃,要是被代表了,可以通过如下方式验证:

  1. Snap1
  2. Snap1

数组排序方法

初一看,数组的排序方法似乎很多,如下图:

Snap2

但是只要我们再认真分析一下,可以发现可以根据是否为泛型,是否带关键字数组将排序方法分成4类,其余的全是重载方法。即:

public static void Sort<T>(T[] array);public static void Sort(Array array);public static void Sort<TKey, TValue>(TKey[] keys, TValue[] items);public static void Sort(Array keys[], Array items);

重载方法包含的参数有:

  • int index,从指定索引位置开始排序
  • int length,从指定索引位置开始,需要排序的元素的个数
  • ICompare<T> comparer,排序时进行比较的对象
  • Comparison<T> comparison,排序时用于比较的委托

非泛型不带关键字数组排序方法

4种方法内部探究

这一系列共有4个方法:

[ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)][__DynamicallyInvokable]public static void Sort(Array array){  if (array == null)    throw new ArgumentNullException("array");  Array.Sort(array, null, array.GetLowerBound(0), array.Length, null);}
 
[ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)][__DynamicallyInvokable][TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]public static void Sort(Array array, int index, int length){  Array.Sort(array, null, index, length, null);}
 
[ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)][__DynamicallyInvokable]public static void Sort(Array array, IComparer comparer){  if (array == null)    throw new ArgumentNullException("array");  Array.Sort(array, null, array.GetLowerBound(0), array.Length, comparer);}
[ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)][__DynamicallyInvokable][TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]public static void Sort(Array array, int index, int length, IComparer comparer){  Array.Sort(array, null, index, length, comparer);}  

比较这4个方法,发现其归根到底都调用了非泛型不带关键字数组排序方法:

[ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)][SecuritySafeCritical]public static void Sort(Array keys, Array items, int index, int length, IComparer comparer){  if (keys == null)    throw new ArgumentNullException("keys");  if (keys.Rank != 1 || items != null && items.Rank != 1)    throw new RankException(Environment.GetResourceString("Rank_MultiDimNotSupported"));  if (items != null && keys.GetLowerBound(0) != items.GetLowerBound(0))    throw new ArgumentException(Environment.GetResourceString("Arg_LowerBoundsMustMatch"));  if (index < keys.GetLowerBound(0) || length < 0)    throw new ArgumentOutOfRangeException(length < 0 ? "length" : "index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));  if (keys.Length - (index - keys.GetLowerBound(0)) < length || items != null && index - items.GetLowerBound(0) > items.Length - length)    throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));  if (length <= 1 || (comparer == Comparer.Default || comparer == null) && Array.TrySZSort(keys, items, index, index + length - 1))    return;  object[] keys1 = keys as object[];  object[] items1 = null;  if (keys1 != null)    items1 = items as object[];  if (keys1 != null && (items == null || items1 != null))    new Array.SorterObjectArray(keys1, items1, comparer).Sort(index, length);  else    new Array.SorterGenericArray(keys, items, comparer).Sort(index, length);}

阅读代码,我们可以发现会首先尝试Array的TrySZSort方法,成功则直接返回。接着当关键字数组为object[]且项数组为空或项数组也为object[]时,将会调用SorterObjectArray的排序方法,否则调用SorterGenericArray的排序方法

TrySZSort方法

TrySZSort的方法代码如下:

[ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail), SecurityCritical][MethodImpl(MethodImplOptions.InternalCall)]PRivate static extern bool TrySZSort(Array keys, Array items, int left, int right);

可以发现该方法由CLR内部实现,但是好在现在CoreCLR已经开源了,我们可以通过CoreCLR大致了解到这个函数是做什么的。经过查找,发现在一个名叫ArrayHelper的类中发现该发现,代码如下:

FCIMPL4(FC_BOOL_RET, ArrayHelper::TrySZSort, ArrayBase * keys, ArrayBase * items, UINT32 left, UINT32 right)    FCALL_CONTRACT;    VALIDATEOBJECT(keys);    VALIDATEOBJECT(items);    _ASSERTE(keys != NULL);    // <TODO>@TODO: Eventually, consider adding support for single dimension arrays with    // non-zero lower bounds.  VB might care.  </TODO>    if (keys->GetRank() != 1 || keys->GetLowerBoundsPtr()[0] != 0)        FC_RETURN_BOOL(FALSE);    _ASSERTE(left <= right);    _ASSERTE(right < keys->GetNumComponents() || keys->GetNumComponents() == 0);    TypeHandle keysTH = keys->GetArrayElementTypeHandle();    const CorElementType keysElType = keysTH.GetVerifierCorElementType();    if (!CorTypeInfo::IsPrimitiveType_NoThrow(keysElType))        FC_RETURN_BOOL(FALSE);    if (items != NULL) {        TypeHandle itemsTH = items->GetArrayElementTypeHandle();        if (keysTH != itemsTH)            FC_RETURN_BOOL(FALSE);   // Can't currently handle sorting different types of arrays.    }    // Handle special case of a 0 element range to sort.    // Consider both Sort(array, x, x) and Sort(zeroLen, 0, zeroLen.Length-1);    if (left == right || right == 0xffffffff)        FC_RETURN_BOOL(TRUE);    switch(keysElType) {    case ELEMENT_TYPE_I1:        ArrayHelpers<I1>::IntrospectiveSort((I1*) keys->GetDataPtr(), (I1*) (items == NULL ? NULL : items->GetDataPtr()), left, right);        break;    case ELEMENT_TYPE_U1:    case ELEMENT_TYPE_BOOLEAN:        ArrayHelpers<U1>::IntrospectiveSort((U1*) keys->GetDataPtr(), (U1*) (items == NULL ? NULL : items->GetDataPtr()), left, right);        break;    case ELEMENT_TYPE_I2:        ArrayHelpers<I2>::IntrospectiveSort((I2*) keys->GetDataPtr(), (I2*) (items == NULL ? NULL : items->GetDataPtr()), left, right);        break;    case ELEMENT_TYPE_U2:    case ELEMENT_TYPE_CHAR:        ArrayHelpers<U2>::IntrospectiveSort((U2*) keys->GetDataPtr(), (U2*) (items == NULL ? NULL : items->GetDataPtr()), left, right);        break;    case ELEMENT_TYPE_I4:        ArrayHelpers<I4>::IntrospectiveSort((I4*) keys->GetDataPtr(), (I4*) (items == NULL ? NULL : items->GetDataPtr()), left, right);        break;    case ELEMENT_TYPE_U4:        ArrayHelpers<U4>::IntrospectiveSort((U4*) keys->GetDataPtr(), (U4*) (items == NULL ? NULL : items->GetDataPtr()), left, right);        break;            case ELEMENT_TYPE_R4:    {        R4 * R4Keys = (R4*) keys->GetDataPtr();        R4 * R4Items = (R4*) (items == NULL ? NULL : items->GetDataPtr());        // Comparison to NaN is always false, so do a linear pass         // and swap all NaNs to the front of the array        left = ArrayHelpers<R4>::NaNPrepass(R4Keys, R4Items, left, right);        if(left != right) ArrayHelpers<R4>::IntrospectiveSort(R4Keys, R4Items, left, right);        break;    };    case ELEMENT_TYPE_I8:        ArrayHelpers<I8>::IntrospectiveSort((I8*) keys->GetDataPtr(), (I8*) (items == NULL ? NULL : items->GetDataPtr()), left, right);        break;    case ELEMENT_TYPE_U8:        ArrayHelpers<U8>::IntrospectiveSort((U8*) keys->GetDataPtr(), (U8*) (items == NULL ? NULL : items->GetDataPtr()), left, right);        break;    case ELEMENT_TYPE_R8:    {        R8 * R8Keys = (R8*) keys->GetDataPtr();        R8 * R8Items = (R8*) (items == NULL ? NULL : items->GetDataPtr());        // Comparison to NaN is always false, so do a linear pass         // and swap all NaNs to the front of the array        left = ArrayHelpers<R8>::NaNPrepass(R8Keys, R8Items, left, right);        if(left != right) ArrayHelpers<R8>::IntrospectiveSort(R8Keys, R8Items, left, right);        break;    };    case ELEMENT_TYPE_I:    case ELEMENT_TYPE_U:        // In V1.0, IntPtr & UIntPtr are not fully supported types.  They do         // not implement IComparable, so searching & sorting for them should        // fail.  In V1.1 or V2.0, this should change.          FC_RETURN_BOOL(FALSE);    default:        _ASSERTE(!"Unrecognized primitive type in ArrayHelper::TrySZSort");        FC_RETURN_BOOL(FALSE);    }    FC_RETURN_BOOL(TRUE);FCIMPLEND

大致可以看出,该方法的作用是对Framework的基本类型进行排序,排序也使用了内省排序。内省排序详见后面。

SorterObjectArray

我们来看一下SorterObjectArray的构造函数及Sort方法:

private object[] keys;private object[] items;private IComparer comparer;internal SorterObjectArray(object[] keys, object[] items, IComparer comparer){    if (comparer == null)    {        comparer = Comparer.Default;    }    this.keys = keys;    this.items = items;    this.comparer = comparer;}

构造函数很简单,仅仅赋值字段

internal void Sort(int left, int length){    if (BinaryCompatibility.TargetsAtLeast_Desktop_V4_5)    {        this.IntrospectiveSort(left, length);        return;    }    this.DepthLimitedQuickSort(left, length + left - 1, 32);}

初看一下代码 ,发现关键语句是BinaryCompatibility.TargetsAtLeast_Desktop_V4_5,其决定了究竟使用了哪种排序方式。从字面意思上是说是否为桌面版本4.5及以上,经过验证后也确实如此,但因为与主题关系不大,就不细说,仅简单说下原理。

编译器在编译程序集时,会加上TargetFramework特性,.Network Framework通过检测该特性来确定框架版本,如下是4.0的一个程序集示例:

Snap3

SorterObjectArray的DepthLimitedQuickSort

DepthLimitedQuickSort从字面意思来看,是深度限制快速排序,其代码如下:

private void DepthLimitedQuickSort(int left, int right, int depthLimit)            {                do                {                    if (depthLimit == 0)                    {                        try                        {                            this.Heapsort(left, right);                            break;                        }                        catch (IndexOutOfRangeException)                        {                            throw new ArgumentException(Environment.GetResourceString("Arg_BogusIComparer", new object[]                            {                                this.comparer                            }));                        }                        catch (Exception innerException)                        {                            throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), innerException);                        }                    }                    int num = left;                    int num2 = right;                    int median = Array.GetMedian(num, num2);                    try                    {                        this.SwapIfGreaterWithItems(num, median);                        this.SwapIfGreaterWithItems(num, num2);                        this.SwapIfGreaterWithItems(median, num2);                    }                    catch (Exception innerException2)                    {                        throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), innerException2);                    }                    object obj = this.keys[median];                    do                    {                        try                        {                            while (this.comparer.Compare(this.keys[num], obj) < 0)                            {                                num++;                            }                            while (this.comparer.Compare(obj, this.keys[num2]) < 0)                            {                                num2--;                            }                        }                        catch (IndexOutOfRangeException)                        {                            throw new ArgumentException(Environment.GetResourceString("Arg_BogusIComparer", new object[]                            {                                this.comparer                            }));                        }                        catch (Exception innerException3)                        {                            throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), innerException3);                        }                        if (num > num2)                        {                            break;                        }                        if (num < num2)                        {                            object obj2 = this.keys[num];                            this.keys[num] = this.keys[num2];                            this.keys[num2] = obj2;                            if (this.items != null)                            {                                object obj3 = this.items[num];                                this.items[num] = this.items[num2];                                this.items[num2] = obj3;                            }                        }                        num++;                        num2--;                    }                    while (num <= num2);                    depthLimit--;                    if (num2 - left <= right - num)                    {                        if (left < num2)                        {                            this.DepthLimitedQuickSort(left, num2, depthLimit);                        }                        left = num;                    }                    else                    {                        if (num < right)                        {                            this.DepthLimitedQuickSort(num, right, depthLimit);                        }                        right = num2;                    }                }                while (left < right);            }

调用时深度为32,每调用一次,深度减1,深度大于0时使用快速排序,当深度为0时即转入堆排序。在使用快速排序时,选取首、尾和首尾中间三个索引中取值为中间的对象作为主元。然后取左右两边较长的序列进入下一轮快速排序。

用到的其余代码如下:

internal void SwapIfGreaterWithItems(int a, int b){    if (a != b && this.comparer.Compare(this.keys[a], this.keys[b]) > 0)    {        object obj = this.keys[a];        this.keys[a] = this.keys[b];        this.keys[b] = obj;        if (this.items != null)        {            object obj2 = this.items[a];            this.items[a] = this.items[b];            this.items[b] = obj2;        }    }}private void Swap(int i, int j){    object obj = this.keys[i];    this.keys[i] = this.keys[j];    this.keys[j] = obj;    if (this.items != null)    {        object obj2 = this.items[i];        this.items[i] = this.items[j];        this.items[j] = obj2;    }}
private void Heapsort(int lo, int hi){    int num = hi - lo + 1;    for (int i = num / 2; i >= 1; i--)    {        this.DownHeap(i, num, lo);    }    for (int j = num; j > 1; j--)    {        this.Swap(lo, lo + j - 1);        this.DownHeap(1, j - 1, lo);    }}private void DownHeap(int i, int n, int lo){    object obj = this.keys[lo + i - 1];    object obj2 = (this.items != null) ? this.items[lo + i - 1] : null;    while (i <= n / 2)    {        int num = 2 * i;        if (num < n && this.comparer.Compare(this.keys[lo + num - 1], this.keys[lo + num]) < 0)        {            num++;        }        if (this.comparer.Compare(obj, this.keys[lo + num - 1]) >= 0)        {            break;        }        this.keys[lo + i - 1] = this.keys[lo + num - 1];        if (this.items != null)        {            this.items[lo + i - 1] = this.items[lo + num - 1];        }        i = num;    }    this.keys[lo + i - 1] = obj;    if (this.items != null)    {        this.items[lo + i - 1] = obj2;    }}

SorterObjectArray的IntrospectiveSort

IntrospectiveSort的代码如下:

private void IntrospectiveSort(int left, int length){    if (length < 2)    {        return;    }    try    {        this.IntroSort(left, length + left - 1, 2 * IntrospectiveSortUtilities.FloorLog2(this.keys.Length));    }    catch (IndexOutOfRangeException)    {        IntrospectiveSortUtilities.ThrowOrIgnoreBadComparer(this.comparer);    }    catch (Exception innerException)    {        throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_IComparerFailed"), innerException);    }}private void IntroSort(int lo, int hi, int depthLimit){    while (hi > lo)    {        int num = hi - lo + 1;        if (num <= 16)        {            if (num == 1)            {                return;            }            if (num == 2)            {                this.SwapIfGreaterWithItems(lo, hi);                return;            }            if (num == 3)            {                this.SwapIfGreaterWithItems(lo, hi - 1);                this.SwapIfGreaterWithItems(lo, hi);                this.SwapIfGreaterWithItems(hi - 1, hi);                return;            }            this.InsertionSort(lo, hi);            return;        }        else        {            if (depthLimit == 0)            {                this.Heapsort(lo, hi);                return;            }            depthLimit--;            int num2 = this.PickPivotAndPartition(lo, hi);            this.IntroSort(num2 + 1, hi, depthLimit);            hi = num2 - 1;        }    }}private int PickPivotAndPartition(int lo, int hi){    int num = lo + (hi - lo) / 2;    this.SwapIfGreaterWithItems(lo, num);    this.SwapIfGreaterWithItems(lo, hi);    this.SwapIfGreaterWithItems(num, hi);    object obj = this.keys[num];    this.Swap(num, hi - 1);    int i = lo;    int num2 = hi - 1;    while (i < num2)    {        while (this.comparer.Compare(this.keys[++i], obj) < 0)        {        }        while (this.comparer.Compare(obj, this.keys[--num2]) < 0)        {        }        if (i >= num2)        {            break;        }        this.Swap(i, num2);    }    this.Swap(i, hi - 1);    return i;}

在其中用到的FloorLog2代码如下:

internal static int FloorLog2(int n){    int num = 0;    while (n >= 1)    {        ++num;        n /= 2;    }    return num;}

其余用到的方法请参见上面的DepthLimitedQuickSort,初始深度为长度的2的对数取下界的2倍,其流程图如下:

Snap4

SorterObjectArray排序的总结

可以看到,不论是DepthLimitedQuickSort还是IntrospectiveSort,都是内省排序的变种。内省排序是一种混合排序算法,以快速排序开始并在超过一定的递归深度后转换为堆排序。解决了快速排序在最坏情况下时间复杂度变为O(n2)的问题。随便说一句,在STL中也使用了内省排序。

至于递归深度的选择,在Framework 4.5以上,是Snap7,以下是32。这正是O(n log n) 。对于32来说,2的32方是4G,而CLR上运行的程序分配的内存不为超过2G。(不要问我是怎么知道的,我们的项目大约在超过1.2个G的时候崩溃过,当然在不同情况下最大可用内存不一样)

SorterGenericArray

SorterGenericArray与SorterObjectArray基本一样,只是将在SorterObjectArray的索引器替换Array的GetValue和SetValue。就不再细说了。

非泛型带关键字数组排序方法

从上面也可以看出,非泛型不带关键字数组排序方法本质上正是调用的非泛型带关键字数组排序的public static void Sort(Array array, int index, int length, IComparer comparer)方法,而非泛型带关键值数组的其余三个方法本质也同样是调用该方法,而该方法在前面已经分析过了,就不再说了。

泛型不带关键字数组排序方法

泛型不带关键字数组排序方法共5个,而最终调用的是public static void Sort<T>(T[] array, int index, int length, IComparer<T> comparer)这个方法。该方法如下:

[SecuritySafeCritical][ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)][__DynamicallyInvokable]public static void Sort<T>(T[] array, int index, int length, IComparer<T> comparer){  if (array == null)    throw new ArgumentNullException("array");  if (index < 0 || length < 0)    throw new ArgumentOutOfRangeException(length < 0 ? "length" : "index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));  if (array.Length - index < length)    throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));  if (length <= 1 || (comparer == null || comparer == Comparer<T>.Default) && Array.TrySZSort((Array) array, (Array) null, index, index + length - 1))    return;  ArraySortHelper<T>.Default.Sort(array, index, length, comparer);}

查看ArraySortHelper<T>代码,发现该类代码也与泛型不带关键字数组排序方法逻辑完全一致,就不再细说了。

泛型带关键字数组排序方法

泛型带关键字数组排序方法共4个,而最终调用的是public static void Sort<TKey, TValue>(TKey[] keys, TValue[] items, int index, int length, IComparer<TKey> comparer)这个方法。该方法如下:

[ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)][SecuritySafeCritical]public static void Sort<TKey, TValue>(TKey[] keys, TValue[] items, int index, int length, IComparer<TKey> comparer){  if (keys == null)    throw new ArgumentNullException("keys");  if (index < 0 || length < 0)    throw new ArgumentOutOfRangeException(length < 0 ? "length" : "index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));  if (keys.Length - index < length || items != null && index > items.Length - length)    throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));  if (length <= 1 || (comparer == null || comparer == Comparer<TKey>.Default) && Array.TrySZSort((Array) keys, (Array) items, index, index + length - 1))    return;  if (items == null)    Array.Sort<TKey>(keys, index, length, comparer);  else    ArraySortHelper<TKey, TValue>.Default.Sort(keys, items, index, length, comparer);}

该方法在items为空时直接调用泛型带关键字数组排序方法,在其他情况下调用ArraySortHelper<TKey, TValue>的排序方法。

查看ArraySortHelper<TKey, TValue>的代码,发现该类代码也与泛型带关键字数组排序方法逻辑完全一致,就不再细说了。

ArrayList

ArrayList这货虽然现在很少用了,但是毕竟了曾风光了一段时间,还是提一下,通过查看其代码,发现底层还是调用的Array的非泛型不带关键字数组的排序方法。这也不奇怪,跟List一样,底层用的都是数组。

public virtual void Sort(int index, int count, IComparer comparer){    if (index < 0)    {        throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));    }    if (count < 0)    {        throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));    }    if (this._size - index < count)    {        throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));    }    Array.Sort(this._items, index, count, comparer);    this._version++;}

List

跟ArrayList类似,其底层调用的是Array的泛型不带关键字数组的排序方法。代码如下:

public void Sort(int index, int count, IComparer<T> comparer){  if (index < 0)    ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);  if (count < 0)    ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.count, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);  if (this._size - index < count)    ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);  Array.Sort<T>(this._items, index, count, comparer);  ++this._version;}
[__DynamicallyInvokable]public void Sort(Comparison<T> comparison){  if (comparison == null)    ThrowHelper.ThrowArgumentNullException(ExceptionArgument.match);  if (this._size <= 0)    return;  Array.Sort<T>(this._items, 0, this._size, (IComparer<T>) new Array.FunctorComparer<T>(comparison));}

Linq排序

OrderBy与OrderByDescending

通过查看代码,发现OrderBy与OrderByDescending基本一致,都使用了OrderedEnumerable<TSource, TKey>类,代码如下:

[__DynamicallyInvokable]public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector){  return (IOrderedEnumerable<TSource>) new OrderedEnumerable<TSource, TKey>(source, keySelector, (IComparer<TKey>) null, false);}[__DynamicallyInvokable]public static IOrderedEnumerable<TSource> OrderBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer){  return (IOrderedEnumerable<TSource>) new OrderedEnumerable<TSource, TKey>(source, keySelector, comparer, false);}[__DynamicallyInvokable]public static IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector){  return (IOrderedEnumerable<TSource>) new OrderedEnumerable<TSource, TKey>(source, keySelector, (IComparer<TKey>) null, true);}[__DynamicallyInvokable]public static IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer){  return (IOrderedEnumerable<TSource>) new OrderedEnumerable<TSource, TKey>(source, keySelector, comparer, true);}

而OrderedEnumerable<TSource, TKey>继承自OrderedEnumerable<TElement>,OrderedEnumerable<TSource, TKey>中排序使用了EnumerableSorter<TElement, TKey>,而EnumerableSorter<TElement, TKey>继承自EnumerableSorter<TElement>。在EnumerableSorter<TElement>我们可以发现排序的关键代码:

internal int[] Sort(TElement[] elements, int count){  this.ComputeKeys(elements, count);  int[] map = new int[count];  for (int index = 0; index < count; ++index)    map[index] = index;  this.QuickSort(map, 0, count - 1);  return map;}private void QuickSort(int[] map, int left, int right){  do  {    int left1 = left;    int right1 = right;    int index1 = map[left1 + (right1 - left1 >> 1)];    while (true)    {      do      {        if (left1 >= map.Length || this.CompareKeys(index1, map[left1]) <= 0)        {          while (right1 >= 0 && this.CompareKeys(index1, map[right1]) < 0)            --right1;          if (left1 <= right1)          {            if (left1 < right1)            {              int num = map[left1];              map[left1] = map[right1];              map[right1] = num;            }            ++left1;            --right1;          }          else            break;        }        else          goto label_1;      }      while (left1 <= right1);      break;label_1:      ++left1;    }    if (right1 - left <= right - left1)    {      if (left < right1)        this.QuickSort(map, left, right1);      left = left1;    }    else    {      if (left1 < right)        this.QuickSort(map, left1, right);      right = right1;    }  }  while (left < right);}

原来是快速排序。

ThenBy和ThenByDescending

查看代码,发现ThenBy和ThenByDescending基本一致,都是传入IOrderedEnumerable<TSource>,返回其CreateOrderedEnumerable方法调用。

[__DynamicallyInvokable]public static IOrderedEnumerable<TSource> ThenBy<TSource, TKey>(this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector){ if (source == null) throw Error.ArgumentNull("source"); else    return source.CreateOrderedEnumerable<TKey>(keySelector, (IComparer<TKey>) null, false);}[__DynamicallyInvokable]public static IOrderedEnumerable<TSource> ThenBy<TSource, TKey>(this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer){ if (source == null) throw Error.ArgumentNull("source"); else    return source.CreateOrderedEnumerable<TKey>(keySelector, comparer, false);}[__DynamicallyInvokable]public static IOrderedEnumerable<TSource> ThenByDescending<TSource, TKey>(this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector){ if (source == null) throw Error.ArgumentNull("source"); else    return source.CreateOrderedEnumerable<TKey>(keySelector, (IComparer<TKey>) null, true);}[__DynamicallyInvokable]public static IOrderedEnumerable<TSource> ThenByDescending<TSource, TKey>(this IOrderedEnumerable<TSource> source, Func<TSource, TKey> keySelector, IComparer<TKey> comparer){ if (source == null) throw Error.ArgumentNull("source"); else    return source.CreateOrderedEnumerable<TKey>(keySelector, comparer, true);}

而在上面,我们已经发现了OrderBy与OrderByDescending都返回了OrderedEnumerable<TSource, TKey>,在其中找到CreateOrderedEnumerable方法。

IOrderedEnumerable<TElement> IOrderedEnumerable<TElement>.CreateOrderedEnumerable<TKey>(Func<TElement, TKey> keySelector, IComparer<TKey> comparer, bool descending){  OrderedEnumerable<TElement, TKey> orderedEnumerable = new OrderedEnumerable<TElement, TKey>(this.source, keySelector, comparer, descending);  orderedEnumerable.parent = this;  return (IOrderedEnumerable<TElement>) orderedEnumerable;}

这说明正是在前面排序的基础上进行再次排序。

参考资料

C#集合--数组 - On the road.... - 博客园

比较排序算法 - 匠心十年 - 博客园

Introsort - Wikipedia, the free encyclopedia:

一并予以感谢


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表