public static void Sort(Array array)
{
if (array == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
}
Sort(array, null, array.GetLowerBound(0), array.Length, null);
}
第一个if判断语句为,判断传入的数组是否为空,从而根据数组异常的类型决定是否抛出异常,这里我们不做深入探讨(我也不会),我们主要看下面的Sort()方法。
public static void Sort(Array keys, Array? items, int index, int length, IComparer? comparer)
该方法传入5个参数
1.Array key:就是你传入的数组 我们可以叫它键数组 keys,为什么怎么叫请继续往下看。
2.Array?itrams:
一个可空的项数组 items(Array?为可空类型,想了解相关知识可自行搜索)。
注意上面的Sort()方法Array?items 参数位置直接写为null,是以为我们使用的是Sort(Array array)的重载,不包含Array?iteams 参数,所以直接写为null。
下面我们使用Sort(Array array,Array?iteam)重载为你解释Array?iteam的作用
主要作用:与你传入的数组一起进行排序
什么意思呢,我们举例来说明
string[] strings = { "c", "a", "e", "b", "d" };
int[] ints = { 4, 1, 5, 2, 3 };
Array.Sort(strings, ints);
foreach (string s in strings)
{
Console.Write(s+" ");
}
Console.WriteLine();
foreach (int i in ints)
{
Console.Write(i+" ");
}
我们传入两个数组,strings里的字母与ints里的数字相对应例如“a”->1;
然后我们使用 Array.Sort(strings, ints);对strings数组进行排序,那么Sort方法会根据strings排序好的顺序,在对ints进行排序。
这也是我们为啥叫.Array key为键数组的原因,因为key[i]与iteam[i]互相对应
3. int index:表示你想要开始排序的位置
而我们Sort(Array array)这个重载中使用了array.GetLowerBound(0)来代替了index。
我们首先来解释一下array.GetLowerBound(0):
array.GetLowerBound(0)
方法用于获取指定数组的第一个维度的下界(索引的开始位置)(lower bound)。
维度:表示你的数组是一维,还是二维数组。
我们都知道多维数组的索引都是从0开始的所以array.GetLowerBound(0)的值始终为0,你无法去改变array.GetLowerBound(0)的值,所以当我们不需要指定需要排序数组的开始位置时使用array.GetLowerBound(0)来代替0,至于为什么,我也不知道是因为安全问题吗?
4. int length:,一个排序的长度 length及数组的长度,在此重载中无需指定长度所以使用array.Length来代替。
5.IComparer? comparer
一个可空的比较器 comparer
作为参数。
那么这个参数到底有什么意义呢?作用又是什么呢?
5.1从IComparer命名来看,这应该是一个接口类型。
我们都知道Array.Sort()方法可以对C#内部的基类进行排序(int,float,string等),但是它无法对我们自己定义的类型进行排序
例如我们创建一个Player类并且定义两个属性。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace C_进阶
{
internal class Player
{
public int hp;
public string name;
public int Hp
{
get { return hp; }
set
{
if (value < 0 || value > 100)
{
Console.WriteLine("Hp赋值错误");
}
else
{
hp = value;
}
}
}
public string Name
{
get { return name; }
set
{
if (value!= null)
{
name= value;
}
else
{
Console.WriteLine("Name为null");
}
}
}
}
}
然后我们创建一个Player数组,我希望可以通过Array.Sort()方法对Player数组中的Hp进行排序
Player[] players =
{
new Player() { Hp = 40, Name = "a" },
new Player() { Hp = 10, Name = "b" },
new Player() { Hp = 70, Name = "c" },
new Player() { Hp = 5, Name = "d" }
};
Array.Sort(players);
显然这样做是不可能的,控制台会报这样一个错误。
内部异常 1:
ArgumentException: At least one object must implement IComparable.
您遇到的问题是“ArgumentException: 至少一个对象必须实现 IComparable 接口
所以我们对Player类实现这个接口就可以对Player数组进行排序
该接口要求我们实现一个CompareTo(object?obj)方法,obj就代表我们传入的想要比较的类型,
而Hp.CompareTo()方法是int类型的自带方法public Int32 CompareTo(Int32 value);
public int CompareTo(int value)
{
if (this < value)
{
return -1;
}
if (this > value)
{
return 1;
}
return 0;
}
这段代码是一个用于比较两个整数的方法。它使用了 CompareTo 方法来将当前整数对象与另一个整数进行比较。
具体代码逻辑如下:
- 首先通过比较当前整数对象
this
和另一个整数value
的大小关系来进行比较。 - 如果当前整数对象小于
value
,则返回 -1,表示当前整数对象在排序上位于value
之前。 - 如果当前整数对象大于
value
,则返回 1,表示当前整数对象在排序上位于value
之后。 - 如果当前整数对象等于
value
,则返回 0,表示两个整数相等。
这种实现方式是基于整数类型的 CompareTo 方法的常规实现方式,用于在排序或比较操作中确定对象之间的相对顺序。
那么为什么是返回0,1,-1呢,按照常理来说不应该是返回那个比较大或小的数吗?
好问题!实际上,返回 1、0 和 -1 是一种约定俗成的规定,用于在比较操作中表示对象之间的相对顺序。返回 1 表示当前对象在排序中应该位于比较对象之后,而返回 -1 则表示当前对象应该位于比较对象之前。而返回 0 表示两个对象相等,它们在排序中应该被视为相同的位置。这种约定可以使比较方法统一地返回某种整数值,使得它们可以方便地用于排序算法和其他比较操作。虽然可以选择返回比较大或比较小的那个数作为结果,但这种约定更符合通用实践和约定,并且在标准的排序算法和集合类中广泛应用。所以,当我们使用 CompareTo 方法进行对象比较时,可以依赖于返回值 1、0 和 -1 来判断相对顺序和相等性。
至于返回的1、0、-1,去了哪里这个我就不知道了,可能是去了另外一个方法体能然后根据1、0、-1,来决定是否交换两个值。
这样我们就可以对我们自己定义的类型进行排序了。
但我们好像偏移了我们的主题ICompare接口,不要着急我们继续往下看。
通过面向对象的思想我们应该知道高内聚低耦合的概念,所以我们不应该对Player这个类去进行修改,我们应该新建一个Player Compare类去完成这个比较,所以微软为我们提供了ICompare接口。
internal class PlayerCompare : IComparer<Player>
{
int IComparer<Player>.Compare(Player? x, Player? y)
{
if (x == null && y == null)
{
return 0; // 两个对象都为空,认为它们相等
}
else if (x == null)
{
return -1; // x 为空,y 不为空,认为 x 小于 y
}
else if (y == null)
{
return 1; // y 为空,x 不为空,认为 x 大于 y
}
else
{
return x.Hp.CompareTo(y.Hp); // 比较两个对象的年龄属性
}
}
}
这里我们使用ICompare<>泛型方法,这样就省去了装箱的过程让代码看起来更加简洁。
要使用ICompare需要使用Array.Sort(Array array,ICompare? compare)重载,Array.Sort(Array array)方法没有使用到这个可空的比较器,所以直接赋值为了null。
下面为大家展示一下源码
public static void Sort(Array array)
{
if (array == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
}
Sort(array, null, array.GetLowerBound(0), array.Length, null);
}
public static void Sort(Array keys, Array? items, int index, int length, IComparer? comparer)
{
if (keys == null)
{
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.keys);
}
if (keys.Rank != 1 || (items != null && items.Rank != 1))
{
ThrowHelper.ThrowRankException(ExceptionResource.Rank_MultiDimNotSupported);
}
int lowerBound = keys.GetLowerBound(0);
if (items != null && lowerBound != items.GetLowerBound(0))
{
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_LowerBoundsMustMatch);
}
if (index < lowerBound)
{
ThrowHelper.ThrowIndexArgumentOutOfRange_NeedNonNegNumException();
}
if (length < 0)
{
ThrowHelper.ThrowLengthArgumentOutOfRange_ArgumentOutOfRange_NeedNonNegNum();
}
if (keys.Length - (index - lowerBound) < length || (items != null && index - lowerBound > items.Length - length))
{
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidOffLen);
}
if (length <= 1)
{
return;
}
if (comparer == null)
{
comparer = Comparer.Default;
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
if (keys is object[] keys2)
{
object[] array = items as object[];
if (items == null || array != null)
{
new SorterObjectArray(keys2, array, comparer).Sort(index, length);
return;
}
}
if (comparer == Comparer.Default)
{
CorElementType corElementTypeOfElementType = keys.GetCorElementTypeOfElementType();
if (items == null || items.GetCorElementTypeOfElementType() == corElementTypeOfElementType)
{
int adjustedIndex2 = index - lowerBound;
switch (corElementTypeOfElementType)
{
case CorElementType.ELEMENT_TYPE_I1:
GenericSort<sbyte>(keys, items, adjustedIndex2, length);
return;
case CorElementType.ELEMENT_TYPE_BOOLEAN:
case CorElementType.ELEMENT_TYPE_U1:
GenericSort<byte>(keys, items, adjustedIndex2, length);
return;
case CorElementType.ELEMENT_TYPE_I2:
GenericSort<short>(keys, items, adjustedIndex2, length);
return;
case CorElementType.ELEMENT_TYPE_CHAR:
case CorElementType.ELEMENT_TYPE_U2:
GenericSort<ushort>(keys, items, adjustedIndex2, length);
return;
case CorElementType.ELEMENT_TYPE_I4:
GenericSort<int>(keys, items, adjustedIndex2, length);
return;
case CorElementType.ELEMENT_TYPE_U4:
GenericSort<uint>(keys, items, adjustedIndex2, length);
return;
case CorElementType.ELEMENT_TYPE_I8:
case CorElementType.ELEMENT_TYPE_I:
GenericSort<long>(keys, items, adjustedIndex2, length);
return;
case CorElementType.ELEMENT_TYPE_U8:
case CorElementType.ELEMENT_TYPE_U:
GenericSort<ulong>(keys, items, adjustedIndex2, length);
return;
case CorElementType.ELEMENT_TYPE_R4:
GenericSort<float>(keys, items, adjustedIndex2, length);
return;
case CorElementType.ELEMENT_TYPE_R8:
GenericSort<double>(keys, items, adjustedIndex2, length);
return;
}
}
}
new SorterGenericArray(keys, items, comparer).Sort(index, length);
static void GenericSort<T>(Array keys, Array items, int adjustedIndex, int length) where T : struct
{
Span<T> span = UnsafeArrayAsSpan<T>(keys, adjustedIndex, length);
if (items != null)
{
span.Sort(UnsafeArrayAsSpan<T>(items, adjustedIndex, length));
}
else
{
span.Sort();
}
}
}
横线上面的代码基本都是做一些安全判断,进行了一些参数校验,确保 keys 数组和 items 数组是一维数组,并且它们的长度和索引范围是合法的。
然后,代码根据给定的比较器(comparer)对数组进行排序。如果没有指定比较器,则使用默认的比较器(Comparer.Default)。
如果 keys 数组是一个对象数组(object[]),并且 items 数组也是一个对象数组或者为空,代码使用 SorterObjectArray 类对它们进行排序。
否则,代码根据 keys 数组元素的类型来选择合适的排序方法。根据元素类型的不同,调用对应的 GenericSort 方法,使用泛型和 Span 对数组进行排序。
最后,代码返回排序后的数组。
本人实力有限只能为大家讲解一下自己的一些理解和Ai的一些补充,如有错误请大家帮忙指出。