0. 完整代码链接
链接:https://pan.baidu.com/s/1NibsPHraeUFSq1uxuioMdQ?pwd=hyw3
提取码:hyw3
1. 基本值类型数据排序(引言)
我们先来看int型数组排序
int[] nums = { 1, 5, 3, 9, 7 };
Console.WriteLine("--- 排序前: ---" + "\n");
foreach (int i in nums)
{
Console.Write(i + " ");
}
Array.Sort(nums);
Console.WriteLine("\n");
Console.WriteLine("--- 排序后: ---" + "\n");
foreach (int i in nums)
{
Console.Write(i + " ");
}
输出:
没有问题
2. 那么问题来了,自定义数据类型可以这么排序吗???
先来看代码
1. Student.cs
public class Student
{
public int Id { get; set; }
public float Score { get; set; }
public override string ToString()
{
return $"Id: {Id}, Score: {Score}";
}
}
2. Main.cs
Student[] stus =
{
new Student(){ Id = 1, Score = 80},
new Student(){ Id = 3, Score = 60},
new Student(){ Id = 5, Score = 90},
new Student(){ Id = 2, Score = 100},
new Student(){ Id = 4, Score = 50},
};
Array.Sort(stus);
运行主函数,报异常
这个异常叫做无效操作异常,或者叫错误操作异常
原因是
在数组中失败的比较了两个元素,
内部异常 —— 参数异常:至少一个对象必须实现可比较接口(IComparable)
3. 那么问题又来了,为什么基本数据类型就可以直接排序,自定义类型就不可以呢???
通过查看 int 源码可以发现
原来int实现了接口IComparable,所以可以排序
这个接口有一个方法
//
// Summary:
// Defines a generalized comparison method that a value type or class implements
// to create a type-specific comparison method for ordering or sorting its instances.
//
// 定义值类型或类实现的通用比较方法,以创建特定于类型的比较方法用于排序,或排序它的实例。
//
// Type parameters:
// T:
// The type of object to compare.
// 需要比较的对象类型
public interface IComparable<in T>
{
//
// Summary:
// Compares the current instance with another object of the same type and returns
// an integer that indicates whether the current instance precedes, follows, or
// occurs in the same position in the sort order as the other object.
// 将当前实例与另一个相同类型的对象进行比较,并返回一个整数,该整数指示当前实例在排序顺序
// 上是在另一个对象之前、之后还是出现在相同的位置。
//
// Parameters:
// other:
// An object to compare with this instance.
// 和这个实例比较的一个对象
// Returns:
// A value that indicates the relative order of the objects being compared. The
// return value has these meanings:
// 指示被比较对象的相对顺序的值。
// 返回值有以下含义:
// Value – Meaning(值含义)
// Less than zero – This instance precedes other in the sort order.
// 小于零 - 此实例在排序顺序上位于其他实例之前。
// Zero – This instance occurs in the same position in the sort order as other.
// 0 - 此实例在排序顺序中出现在与其他实例相同的位置。
// Greater than zero – This instance follows other in the sort order.
// 大于零 - 这个实例按照排序顺序跟在其他实例后面。
int CompareTo(T? other);
}
而 int 重写了这个方法
public int CompareTo(object? value)
{
if (value == null)
{
return 1;
}
if (value is int i)
{
if (m_value < i) return -1;
if (m_value > i) return 1;
return 0;
}
throw new ArgumentException(SR.Arg_MustBeInt32);
}
所以可以排序,既然如此自定义类型 Student 也实现这个接口(IComparable)不就可以了吗
代码如下:
public class Student : IComparable<Student>
{
public int Id { get; set; }
public float Score { get; set; }
public override string ToString()
{
return $"Id: {Id}, Score: {Score}";
}
public int CompareTo(Student? other)
{
if (other == null) return -1;
return this.Score > other.Score ? 1 : -1;
}
}
运行如下代码:
Student[] stus =
{
new Student(){ Id = 1, Score = 80},
new Student(){ Id = 3, Score = 60},
new Student(){ Id = 5, Score = 90},
new Student(){ Id = 2, Score = 100},
new Student(){ Id = 4, Score = 50},
};
Console.WriteLine("排序前: ");
foreach (Student stu in stus)
{
Console.WriteLine(stu);
}
Array.Sort(stus);
Console.WriteLine("排序后: ");
foreach (Student stu in stus)
{
Console.WriteLine(stu);
}
结果图
这里按照学生成绩升序排序!!!
完美解决!
4. 问题又来了,C#功能这么强大,Sort方法难道没有重载?
翻看源码得知,原来Sort有这么多重载方法
其中有一个方法Sort(Array array, IComparer? comparer),这是什么?
5. IComparer比较器
翻看IComparer源码发现这是一个接口,里面有一个方法
public interface IComparer<in T>
{
int Compare(T? x, T? y);
}
能否如法炮制,自定义一个类传入这个接口对象,实现自定义类型排序呢
答案肯定可以!
public class StudentComparer : IComparer<Student>
{
public int Compare(Student? x, Student? y)
{
if (x == null || y == null) return 0;
return x.Score.CompareTo(y.Score);
}
}
6. CompareTo方法源码
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int CompareTo(float value)
{
if (m_value < value) return -1;
if (m_value > value) return 1;
if (m_value == value) return 0;
// At least one of the values is NaN.
if (IsNaN(m_value))
return IsNaN(value) ? 0 : -1;
else // f is NaN.
return 1;
}
Student.cs
public class Student
{
public int Id { get; set; }
public float Score { get; set; }
public override string ToString()
{
return $"Id: {Id}, Score: {Score}";
}
}
Main.cs
Student[] stus =
{
new Student(){ Id = 1, Score = 80},
new Student(){ Id = 3, Score = 60},
new Student(){ Id = 5, Score = 90},
new Student(){ Id = 2, Score = 100},
new Student(){ Id = 4, Score = 50},
};
Console.WriteLine("排序前: ");
foreach (Student stu in stus)
{
Console.WriteLine(stu);
}
Array.Sort(stus, new StudentComparer());
Console.WriteLine("排序后: ");
foreach (Student stu in stus)
{
Console.WriteLine(stu);
}
输出结果与 标题3结果图一致!
7. 面向接口编程
前面两种方法从概念来说都属于面向接口编程
一个是类实现IComparable
一个是类实现IComparer
8. 用委托实现
其实还提供了一个 Sort 方法
public static void Sort<T>(T[] array, Comparison<T> comparison)
这里 Comparison 是一个委托
public delegate int Comparison<in T>(T x, T y);
所以代码可以这么改写:
public static void Main(string[] args)
{
Student[] stus =
{
new Student(){ Id = 1, Score = 80},
new Student(){ Id = 3, Score = 60},
new Student(){ Id = 5, Score = 90},
new Student(){ Id = 2, Score = 100},
new Student(){ Id = 4, Score = 50},
};
Console.WriteLine("排序前: ");
foreach (Student stu in stus)
{
Console.WriteLine(stu);
}
Array.Sort(stus, CompareByScore);
Console.WriteLine("排序后: ");
foreach (Student stu in stus)
{
Console.WriteLine(stu);
}
}
static int CompareByScore(Student a, Student b)
{
return a.Score > b.Score ? 1 : -1;
}
这里CompareByScore不一定要被static修饰,因为Main被static修饰了,所以CompareByScore也要被static修饰,否则会报错,因为静态方法里不能调用实例方法
输出结果:
9. 用Lambda表达式实现
委托都用了,Lambda表达式更可以用!
改写代码如下
Student[] stus =
{
new Student(){ Id = 1, Score = 80},
new Student(){ Id = 3, Score = 60},
new Student(){ Id = 5, Score = 90},
new Student(){ Id = 2, Score = 100},
new Student(){ Id = 4, Score = 50},
};
Console.WriteLine("排序前: ");
foreach (Student stu in stus)
{
Console.WriteLine(stu);
}
Array.Sort(stus, (Student stu1, Student stu2) =>
{
return stu1.Score > stu2.Score ? 1 : -1;
});
Console.WriteLine("排序后: ");
foreach (Student stu in stus)
{
Console.WriteLine(stu);
}
输出结果不变:
10. (扩展)冒泡排序
10.1 普通版
void BubbleSort(int[] nums)
{
int len = nums.Length;
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
if (nums[j] > nums[j + 1])
{
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
}
看了这篇帖子,以后就不要这么写了,不通用!
10.2 通用版
/// <summary>
/// 这里condition作为条件委托参数传入, 您可以自定义专属于自己的冒泡排序条件!!!
/// </summary>
/// <typeparam name="T"> 参数类型 </typeparam>
/// <param name="array"> 数组变量名 </param>
/// <param name="contion"> 条件 </param>
static void BubbleSort<T>(T[] array, Func<T, T, bool> condition)
{
int len = array.Length;
for (int i = 0; i < len - 1; i++)
{
for (int j = 0; j < len - i - 1; j++)
{
if (condition(array[j], array[j + 1]))
{
T temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
}
}
10.3 测试代码
int[] nums = { 1, 5, 7, 3, 9 };
Console.WriteLine("排序前: ");
foreach (var item in nums)
{
Console.Write(item + " ");
}
BubbleSort<int>(nums, (a, b) =>
{
return a > b;
});
Console.WriteLine("\n\n排序后: ");
foreach (var item in nums)
{
Console.Write(item + " ");
}
输出结果: