.Net中集合排序的一種高級玩法


背景:

學生有名稱、學號,

班級有班級名稱、班級序號

學校有學校名稱、學校編號(序號)

 

需求 

 

現在需要對學生進行排序

 

第一排序邏輯

  1. 按學校編號(序號)排列
  2. 再按班級序號排列
  3. 再按學生學號排列

 

 

當然,在我們錄入數據庫信息的時候,有的人可能比較懶,沒有錄入 學校的序號, 班級的序號,學生的學號 ,怎么辦?  那么就Plan B  !

 

第二排序邏輯

 

  1. 按學校名稱排列
  2. 再按班級的名稱排列
  3. 再按學生名稱排列

 

我編寫了學校、班級、學生的實體關系代碼如下:

 

namespace Sort
{
    public class School
    {
        public int? Order { get; set; }
        public string Name { get; set; }
    }

    public class Class
    {
        public int? Order { get; set; }

        public string Name { get; set; }

        public School School { get; set; }
    }

    public class Student
    {
        public int? Order { get; set; }

        public string Name { get; set; }

        public Class Class { get; set; }

    }
}

 

 

以前寫的簡單排序,還可以用OrderBy解決,如果之前寫過的一篇文章:

《.Net中集合排序還可以這么玩》

但是這里的排序就比較復雜了,用簡單的OrderBy恐怕是解決不了了。

Sort

.Net中,對List集合,有一個Sort字方法,讓我們選中Sort方法,F12,看看Sort方法長哪樣?

 

可以看到Sort方法一共有四個重載,我們挑最基礎的一個,Sort()  0參數的這個,懂了這個,其他幾個應該也會懂了,我們看看該方法的描述:

 

 

雖然我英語不太好,但是這基礎的英語還是能看懂,大致是說:

 

用默認的比較器對該List進行排序。

 

那么,這個Comparer(比較器)是什么呢?

IComparable接口

其實,它是接口IComparable下的一個方法,也就是說只有實現了ICoparable接口下的這個叫比較器的方法才能使用Sort進行排序,我們F12進入到IComparable來看看這個接口:

 

可以看到,該接口只有一個CompareTo方法,我用我蹩腳的英語大致看懂了這句話的意思是:

 

定義一個比較方法來對制定類型進行排序。

 

該方法返回類型為Int類型。通過查找查找相關資料,了解到其返回值與其含義如下:

 

含義

復數

該實例比傳入的Other實例小。

0

該實例與傳入的Other實例相等。

正數

該實例比傳入的Other實例大。

 

 

知道了這個原則,我們就可以給Student類繼承並實現該方法了。

 

對文章開頭的排序需求,我們重溫一下:

 

第一排序邏輯(Int?)

  1. 按學校編號(序號)排列
  2. 再按班級序號排列
  3. 再按學生學號排列

 

 

當序號為空時,用第二種排序邏輯,

 

第二排序邏輯(String)

 

    1. 按學校名稱排列
    2. 再按班級的名稱排列
    3. 再按學生名稱排列

 

其實無非就是對Student中涉及到的Int?和string兩種數據類型進行比較。

 

Int?類型(Nullable)和string已經實現了Compare方法,其中Nullable的如下:

 

但是為了能更深入地理解該方法的使用,我自己來寫一個Int?類型數據比較的方法,如下:

 

        private int CompareInit(int? x, int? y)
        {
            if (x == null && y == null)  //如果都是空 那么返回0相等
                return 0;

            if (x.HasValue && y == null)  //如果傳入X有值,但是Y是空的,那么X比Y小 返回-1。
                return -1;

            if (x == null && y.HasValue) //如果傳入X為空,但是Y有值,那么X比Y大 返回1。
                return 1;

            if (x.Value > y.Value)
                return 1;

            if (x.Value < y.Value)
                return -1;

            return 0;                    //否則兩個數相等
        }

其中,為什么我認為有值的比Null的還小返回-1呢? 因為我想把Null的往后排,把有值的往前排,其他流行的做法是認為有值的是比Null大的,即返回1,大家可以結合自己的業務需求選擇。

寫好了Int?類型數據比較的方法,還有String類型數據的比較,我就不自己造輪子去寫了,用現成的String.CompareOrdinal()方法。

 

然后,我們開始給Student實現ICompare接口的CompareTo方法,如下:

 public class Student : IComparable<Student>
    {
        public int? Order { get; set; }

        public string Name { get; set; }

        public Class Class { get; set; }

        public int CompareTo(Student other)
        {
            if (ReferenceEquals(this, other)) return 0;  //如果兩個值的引用相同,那么直接返回相等。
            if (ReferenceEquals(null, other)) return 1;  //如果該實例是空的,但是傳入的實例不是空的,那么返回1

            //比較學校的序號
            var compareResult = CompareInit(this.Class.School.Order, other.Class.School.Order);
            if (compareResult != 0) return compareResult;

            //比較班級的序號
            compareResult = CompareInit(this.Class.Order, other.Class.Order);
            if (compareResult != 0) return compareResult;

            //比較學生的學號
            compareResult = CompareInit(this.Order, other.Order);
            if (compareResult != 0) return compareResult;
            
            //如果以上還未區分出大小,比較學校的名稱
            compareResult = String.CompareOrdinal(this.Class.School.Name, other.Class.School.Name);
            if (compareResult != 0) return compareResult;

            //比較班級的名稱
            compareResult = String.CompareOrdinal(this.Class.Name, other.Class.Name);
            if (compareResult != 0) return compareResult;

            //比較學生的名稱
            return String.CompareOrdinal(this.Name, other.Name);

        }

 

實現該方法后,就可以對List<Student> 使用Sort方法了,我們來試試看。

 

using System;
using System.Collections.Generic;

namespace Sort
{
    class Program
    {
        static void Main(string[] args)
        {
            var students = InitData();

         students.Sort(); //此處執行了Sort方法


            Console.WriteLine("Name-Order");

            foreach (var student in students)
            {
                Console.WriteLine($"學校:{student.Class.School.Name}-{student.Class.School.Order}>>班級:{student.Class.Name}-{student.Class.Order}>>學生:{student.Name}-{student.Order}");
            }

            Console.ReadLine();
        }

        static List<Student> InitData() //創建數據
        {
            var school1 = new School()
            {
                Order = 1,
                Name = "A",

            };
            var school2 = new School
            {
                Name = "B",
                Order = 0
            };

            var class1 = new Class
            {
                Order = 1,
                Name = "1",
                School = school1,
            };

            var class2 = new Class
            {
                Order = 2,
                Name = "2",
                School = school1,
            };

            var class3 = new Class
            {
                Order = 1,
                Name = "1",
                School = school2,
            };

            var student1 = new Student
            {
                Order = 1,
                Name = "1",
                Class = class1,
            };


            var student2 = new Student
            {
                Order = 2,
                Name = "2",
                Class = class1,
            };


            var student3 = new Student
            {
                Order = 3,
                Name = "3",
                Class = class1,
            };


            var student4 = new Student
            {
                Order = 1,
                Name = "1",
                Class = class2,
            };


            var student5 = new Student
            {
                Order = 1,
                Name = "1",
                Class = class3,
            };

            return new List<Student> { student5, student3, student4, student2, student1 };

        }
    }
}

執行效果如下:

可以看到,學校B雖然是以B開頭,但是因為期Order為0比1更靠前,所以以Order為准,學校B排到了最前面。

 

好幾天沒寫了,寫博客對我我而言,意義無非在於加深印象同時把我所掌握的東西分享給大家,這個方法是今天同事教我的,可能對園子里大神來說這比較基礎,但是相信如果掌握這個方法,對以后也許中復雜排序會有比較有用,希望對大家也能有所幫助。

項目的GitHub地址:

https://github.com/liuzhenyulive/Sort

 

如果大家有更好的辦法,歡迎下下方留言與我交流。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM