程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> 關於C# >> .Net編程接口剖析系列之比較和排序

.Net編程接口剖析系列之比較和排序

編輯:關於C#

我們知道,與C++相比較,C#以及整個.Net並不支持多繼承,而相應的,C#支持了接口,並且支持一個類型實現多個接口。對於接口的概念,相信大部分讀者已經有了很好的了解,而我這裡談談個人對於接口理解,只求拋磚引玉。

在我認為,一個接口就是一個對類型的某種能力的認證,並且是以某種標准化的形式將這種能力規范出來。你的類型實現了某個接口,換而言之,也就是說這個類型具備了此接口所標識的能力。比如現在出國留學考托福GRE,開車考駕照這些東西,其實就是相當於我們編程中接口;從某種意義上說,你通過了GRE,就說明你具備在國外學習所需要的語言能力,而你考取了駕照,就證明了你具有上路行駛的能力了。接口同樣如此,給你類型實現特定的一些接口,就是給他們標記了他們所具備的特別能力,而一些依賴這些能力的功能,得以用通用的代碼實現重用,實現可擴展。

我的這個關於接口的系列文章,主要是對.Net編程一些非常重要的接口來進行詳細講解,深入了解這些接口的原理和應用。這對於我們寫出精簡優美的代碼,是非常有幫助的;畢竟,我們在知道自己想做什麼之後,首先應該知道.Net Framework能給我們做什麼。

在本篇以及後續的幾篇文章我們將會談到以下幾個主題:

(一)比較和排序(IComparable和IComparer)

(二)枚舉(IEnumerable和IEnumerator)

(三) 序列化(ISerializable和IXmlSerializable)

System.IComparable & System.IComparable<T>

顧名思義,一個實現了IComparable的class應該就是一個可以對實例進行相互比較的class,我們先來看看它的定義:

[ComVisible(true)]
public interface IComparable
{
int CompareTo(object obj);
}

這個接口相當簡單,只提供了一個接口函數:CompareTo,如果當前對象比被比較的對象小,那麼返回負數;如果相當,則返回0;如果當前對象比被比較的對象大,則返回正數。

但是,如果你覺得這個接口僅僅是能夠讓你比較兩個對象大小,那麼你就錯了,這個接口更大的作用是能夠實現了該類型線性數據結構的排序功能。比如List<T>.Sort()和Array的靜態方法Sort都能夠很好地利用IComparable來對數據進行排序,排序算法由類庫實現,對於我們來說,只需要讓自己的類型實現IComparable接口,負責比較兩個對象大小的算法就可以了。

IComparable<T>是一個泛型接口,用於實現對特定類型的對象的比較,用法和IComparable基本一致,這裡不再進行贅述,下面的例子也是根據IComparable來寫的。

我們來看看下面的代碼,這裡定義了一個學生類Student,每個學生有自己名字和分數。Student類實現了IComparable接口,兩個學生之間直接按照名字進行比較。順便說明Scores類用於存儲學生的成績。

public enum SubjectEnum
{
Total =0,
Chinese,
English,
Math,
}
  
public class Scores //分數類,用於存儲分數
{
int[] _score = new int[4];
public int this[SubjectEnum score]
{
get { return _score[(int)score]; }
set { _score[(int)score] = value; }
}
public override string ToString()
{
string str = "";
foreach (int score in _score)
{
str += "  " + score.ToString();
}
  
return str;
}
}
  
public class Student:IComparable //學生類
{
  
string _name;
  
public string Name
{
get { return _name; }
set { _name = value; }
}
  
Scores _scores=new Scores();
  
public Scores Scores
{
get { return _scores; }
set { _scores = value; }
}
  
public Student(string name,int chinese, int english, int math)
{
_name = name;
  
_scores[SubjectEnum.Chinese] = chinese;
_scores[SubjectEnum.English] = english;
_scores[SubjectEnum.Math] = math;
_scores[SubjectEnum.Total] = chinese +english +math;
}
  
public override string ToString()
{
return _name + _scores.ToString();
}
  
#region IComparable Members
  
public int CompareTo(object obj)
{
if (!(obj is Student))
throw new ArgumentException("Argument not a Student", "obj");
  
return Name.CompareTo(((Student)obj).Name);
}
  
#endregion
}

來看看我們的Main函數,我們在一個數組中存儲了若干個學生,並且利用了Array.Sort對起進行了排序。

static void Main(string[] args)
{
Student[] students = new Student[4];
students[0] = new Student("Michale", 80, 90, 70);
students[1] = new Student("Jack", 90, 80, 75);
students[2] = new Student("Alex", 88, 85, 95);
students[3] = new Student("Rose", 92, 91, 65);
  
Array.Sort(students);
  
Console.WriteLine("Name  Total  Chinese  English  Math");
foreach (Student student in students)
{
Console.WriteLine(student);
}
  
Console.ReadKey();
}

下面來看看輸出結果:

Name Total Chinese English Math Alex  268  88 85  95 Jack 245 90  80  75 Michale 240 80 90 70 Rose 248   92 91 65

 

可以發現,學生們被很好的按照名稱字母的順序進行了排序,並且從小到大地打印出來了。但是我們這裡還是要留下一個問題,假如我們有時候需要按照某項成績進行排序又如何實現呢?假如我們排序的時候希望按照降序進行排列又該如何呢?呵呵,聰明的讀者可能已經想到了,這正是我下一節想要說的內容。

System.Collections.IComparer & System.Collections.Generic. IComparer<T>

IComparer是這麼樣的一個接口,它是用於實現一個專門的“比較器”,這個比較器可以對傳入的兩個對象比較大小。我們來看看它的定義:

[ComVisible(true)]
public interface IComparer
{
int Compare(object x, object y);
}

大家可能會對IComparer存在的必要性有點疑問,那就是既然我們有了IComparable就能夠實現對象的比較以及排序,那麼還需要IComparer做什麼呢,豈不是畫蛇添足?我的回答是:不,IComparer的存在很有必要,因為它可以用來實現一些專門的和功能更加強大的比較器。就如現代社會的分工一樣,以前落後的小農經濟一去不復返了,社會上的各成員要進行相互協作才能發揮最高的效率;同樣,我們設立專業的IComparer,使得比較的功能得以擴展和專業化,你有了更多的選擇。將對象進行比較的時候,你可以使用不同的IComparer來使用不同的方法來比較,就像我們購買商品選擇不同的品牌一樣(試想這件東西不是購買的而是你自己生產的話,那麼你就失去了選擇的機會了)。另外專門的IComparer也可以提供一些屬性,來讓我們的比較變得更加靈活。

光說太抽象,我們下面還是繼續上一節對學生進行排序的問題進行討論。這裡我們可以創建一個專門的學生比較類StudentComparer, 而它則實現了IComparer的泛型接口System.Collections.Generic.IComparer<Student>,StudentComparer的作用是根據成績對學生進行比較。為了將IComparer的優越性體現出來,我們這裡在StudentComparer的構造函數中增加了兩個參數subject和reverse,前者用於指定我們要按照何種科目成績進行比較,而後者則指定是否將結果取反(當然我們也可以使用Array.Reverse方法來將結果按照降序排列,這裡只是實現方法之一)。好,這樣我們比較器就這樣設計好了,看看下面的代碼:

public class StudentComparer: System.Collections.Generic.IComparer<Student>
{
SubjectEnum _subject;
bool _reverse;
  
public StudentComparer(SubjectEnum subject, bool reverse)
{
_subject = subject;
_reverse = reverse;
}
  
#region IComparer<Student> Members
  
public int Compare(Student left, Student right)
{
if (left == null && right == null)
return 0;
else if (left == null)
return -1;
else if (right == null)
return 1;
  
//比較響應科目的成績
int result = left.Scores[_subject].CompareTo(right.Scores[_subject]);
  
//如果反序,只要將結果取反即可
if (_reverse) result = -result;
return result;
}
  
#endregion
}

一個功能強大的比較器就這樣實現了,那麼接下來我們就來實現將學生按照總分進行從高到底的排序,這裡我們只需要對main函數進行稍微的修改就可以了,使用Array.Sort的另外一個重載方法Array.Sort (T[], Generic IComparer) 來進行比較。

看到上面我們在StudentComparer的構造函數中傳入了Total(總分)和True(降序),我們看看執行結果:

Name Total Chinese English Math Alex  268    88  85  95 Rose  248 92 91 65 Jack  245 90 80 75 Michale 240 80      90 70

                         

太棒了,IComparer是這樣的神奇,想象一下如果沒有IComparer而僅僅要用IComparable來實現上面的功能,將是多麼麻煩的事情,更加重要的是,那會將Student類的代碼變的一團糟,就如同一個上班族卻天天要想著回家給自己種的蔬菜澆澆水,給自己養的豬喂喂食一樣,這些瑣碎的東西會讓你的生活一團糟的。

.Net的庫類的排序功能是如此強大,以至於我們還能夠利用代理來進行排序(其實是將比較功能寫在自己的專門函數中),但是本文的重點是講解接口,所以這裡對利用代理排序不再詳述,只是提一下而已。

我們從IComparable和IComparer上學到的,應當不僅僅是比較和排序,而更加應該學到一種思路,一種設計模式,這才是最重要的;另外,它們還有助於加深我們對接口的理解。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved