当前位置: 首页 > 图文教程 > 网络编程 > ASP.NET > .Net编程接口剖析系列之比较和排序

ASP.NET
使用函数传递参数来执行相应的数据库操作
如何实现在窗体和窗体之间进行传递数据
ASP.NET中文显示之两种解决方法
ASP.NET、JSP及PHP之间的抉择
ASP.NET 2.0发送电子邮件中存在的问题
谈谈HtmlControl与WebControl的区别与用途
从ASP.NET 1.1升级到ASP.NET 2.0要考虑的Cookie问题
通过系统配置来提高ASP.NET应用程序的稳定性
妙用ASP2.0中的URL映射改变网址
AJAX实现web页面中级联菜单的设计
ASP.NET跨页面传值技巧总结
再议ASP.NET DataGrid控件中的“添加新行”功能
Geometry 对象浅析
重构CollapsibleSplitter
如何利用.NET Framework使用RSS feed
ASP.NET获取IP与MAC地址的方法
在ASP.NET 2.0中使用样式、主题和皮肤
ASP.NET中为GridView添加删除提示框
ASP.NET 2.0,无刷新页面新境界
看看一个.net版对话框控件

ASP.NET 中的 .Net编程接口剖析系列之比较和排序


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-09-23   浏览: 142 ::
收藏到网摘: n/a

我们知道,与C++相比较,C#以及整个.Net并不支持多继承,而相应的,C#支持了接口,并且支持一个类型实现多个接口。对于接口的概念,相信大部分读者已经有了很好的了解,而我这里谈谈个人对于接口理解,只求抛砖引玉。

在我认为,一个接口就是一个对类型的某种能力的认证,并且是以某种标准化的形式将这种能力规范出来。你的类型实现了某个接口,换而言之,也就是说这个类型具备了此接口所标识的能力。比如现在出国留学考托福GRE,开车考驾照这些东西,其实就是相当于我们编程中接口;从某种意义上说,你通过了GRE,就说明你具备在国外学习所需要的语言能力,而你考取了驾照,就证明了你具有上路行驶的能力了。接口同样如此,给你类型实现特定的一些接口,就是给他们标记了他们所具备的特别能力,而一些依赖这些能力的功能,得以用通用的代码实现重用,实现可扩展。

我的这个关于接口的系列文章,主要是对.Net编程一些非常重要的接口来进行详细讲解,深入了解这些接口的原理和应用。这对于我们写出精简优美的代码,是非常有帮助的;毕竟,我们在知道自己想做什么之后,首先应该知道.Net Framework能给我们做什么。

在本篇以及后续的几篇文章我们将会谈到以下几个主题:

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

(二)枚举(IEnumerable和IEnumerator)

(三) 序列化(ISerializable和IXmlSerializable)

System.IComparable & System.IComparable<T>

顾名思义,一个实现了IComparable的class应该就是一个可以对实例进行相互比较的class,我们先来看看它的定义:

这个接口相当简单,只提供了一个接口函数: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上学到的,应当不仅仅是比较和排序,而更加应该学到一种思路,一种设计模式,这才是最重要的;另外,它们还有助于加深我们对接口的理解。