《Effective C#》 Item 26:对需要排序的对象实现IComparable和IComparer接口
来源:互联网 发布:朗读录音软件 编辑:程序博客网 时间:2024/04/29 11:03
IComparable接口包含一个方法:CompareTo()。这个方法的历史可以追溯到C语言类库中的strcmp函数:如果当前对象小于比较对象则返回小于0的值,如果相等则返回0,如果当前对象大于比较对象则返回大于0的值。IComparable的参数为System.Object型,因此我们在使用的时候需要对参数的类型进行检查:
public struct Customer : IComparable { private readonly string _name; public string Name { get { return _name; } } public Customer(string name) { _name = name; } IComparable 成员#region IComparable 成员 int IComparable.CompareTo(object right) { if (!(right is Customer)) { throw new ArgumentException("Argument not a customer", "right"); } Customer rightCustomer = (Customer)right; return _name.CompareTo(rightCustomer.Name); } #endregion public int CompareTo(Customer right) { return _name.CompareTo(right.Name); } }
IComparable接口有一些不尽人意之处。我们必须检查输入参数的类型,因为我们不能确定使用者会传递哪种类型的对像。另外在对值类型进行操作的时候还会因为boxing和unboxing而降低执行效率。当我们使用IComparable对集合进行排序时需要进行N*log(n)次比较。对于一个大小为1000的集合来说,需要进行7000次左右的比较,在某些条件下这相当于20000多次boxing和unboxing。在这种情况下我们必须寻找更好的比较途径。虽然我们不能修改IComparable.CompareTo()的定义,但是我们还是可以通过重写CompareTo方法来解决这个问题:
public struct Customer : IComparable { private readonly string _name; public string Name { get { return _name; } } public Customer(string name) { _name = name; } IComparable 成员#region IComparable 成员 int IComparable.CompareTo(object right) { if (!(right is Customer)) { throw new ArgumentException("Argument not a customer", "right"); } Customer rightCustomer = (Customer)right; return _name.CompareTo(rightCustomer.Name); } #endregion public int CompareTo(Customer right) { return _name.CompareTo(right.Name); } }
现在IComparable.CompareTo()只是一个接口实现,只能通过IComparable接口被调用。用户使用的将是类型安全的比较。不正确的比较类型不能通过编译。不能通过编译的原因是参数类型和Customer.CompareTo(Customer right)方法不符。只有通过IComparable接口才会调用CompareTo(object right)方法。
当我们实现IComparable接口时,应当也提供一个强类型的重载。这样可以减少我们发生错误的可能性,而且比较两个已知类型对像的效率较高。不过这种重载对于集合的Sort()方法来说没有效果,因为它是通过接口来访问CompareTo()方法的。
我们可以再对Customer结构做一些修改。在C#中允许我们重载运算符。它们调用的是类型安全的CompareTo()方法:
public struct Customer : IComparable { private readonly string _name; public string Name { get { return _name; } } public Customer(string name) { _name = name; } IComparable 成员#region IComparable 成员 int IComparable.CompareTo(object right) { if (!(right is Customer)) { throw new ArgumentException("Argument not a customer", "right"); } Customer rightCustomer = (Customer)right; return _name.CompareTo(rightCustomer.Name); } #endregion public int CompareTo(Customer right) { return _name.CompareTo(right.Name); } public static bool operator <(Customer left, Customer right) { return left.CompareTo(right) < 0; } public static bool operator <=(Customer left, Customer right) { return left.CompareTo(right) <= 0; } public static bool operator >(Customer left, Customer right) { return left.CompareTo(right) > 0; } public static bool operator >=(Customer left, Customer right) { return left.CompareTo(right) >= 0; } }
上例中我们完成了以姓名对用户排序。现在我们又需要对用户的收入进行排序,
而且不可以修改上例中对姓名排序的基本排序方式。为了解决这个问题,
我们可以创建一个实现IComparer接口的类来达到目的。
在.Net类库中凡是实现了IComparable接口的类型都通过IComparer接口进行了重载。
我们可以为Customer接口内部创建一个私有的类,并将其通过静态属性暴露出来:
public struct Customer : IComparable { private readonly string _name; public string Name { get { return _name; } set { _name = value; } } private double _revenue; public double Revenue { get { return _revenue; } set { _revenue = value; } } private static RevenueComparer _revComp = null; public static IComparer RevenueCompare { get { if (_revComp == null) { _revComp = new RevenueComparer(); } return _revComp; } } IComparable 成员#region IComparable 成员 int IComparable.CompareTo(object right) { if (!(right is Customer)) { throw new ArgumentException("Argument not a customer", "right"); } Customer rightCustomer = (Customer)right; return _name.CompareTo(rightCustomer.Name); } #endregion public int CompareTo(Customer right) { return _name.CompareTo(right.Name); } private class RevenueComparer : IComparer { IComparer 成员#region IComparer 成员 int IComparer.Compare(object left, object right) { if (!(left is Customer)) { throw new ArgumentException("Argument not a customer", "left"); } if (!(right is Customer)) { throw new ArgumentException("Argument not a customer", "right"); } Customer leftCustomer = (Customer)left; Customer rightCustomer = (Customer)right; return leftCustomer._revenue.CompareTo(rightCustomer._revenue); } #endregion } }
现在我们就可以通过收入来对用户排序了:
Customer c1 = new Customer(); Customer c2 = new Customer(); Customer.RevenueCompare.Compare(c1, c2);
另外我们还应当注意一下Equals()方法和==运算符。在排序时我们没有实现等于关系的必要。事实上,对于引用类型来说,判断大小依照的是对像的内容,而判断相等依照的是对像的地址。
IComparable和IComparer是为类提供顺序关系的基本机制。IComparable适用于大多数的排序需求。当我们实现它时,也应该重载比较关系运算符。IComparable.CompareTo()使用System.Object类型对像做为参数,为此我们可以提供一个特定类型参数的重载来提高效率,减少错误。如果一个类型并未给我们提供需要的排序机制,那么我们可以通过ICompare来达到目的。
- 《Effective C#》 Item 26:对需要排序的对象实现IComparable和IComparer接口
- Effective C# 使用IComparable和IComparer接口实现排序关系
- 对象的比较与排序:IComparable和IComparer接口
- 对象的比较与排序:IComparable和IComparer接口
- Effective C# 原则26:用IComparable和IComparer实现对象的顺序关系
- 使用IComparable和IComparer接口对集合进行排序
- IComparable和Icomparer接口
- IComparable和IComparer接口
- IComparable 和IComparer 接口实现数组的自定义排列
- 笔记(显示实现接口,实现IComparable,IComparer接口来排序)
- 构建可比较对象IComparable接口和IComparer接口
- C#中IComparable接口和IComparer接口应用的实例
- c# 实现IComparable、IComparer接口、Comparer类的详解
- C#笔记25:比较和排序(IComparable和IComparer以及它们的泛型实现)
- 比较和排序(IComparable和IComparer以及它们的泛型实现)
- 比较和排序(IComparable和IComparer以及它们的泛型实现)
- 比较和排序(IComparable和IComparer以及它们的泛型实现)
- 比较和排序(IComparable和IComparer以及它们的泛型实现)
- 谷歌的产品很多,有的真的没有听说过,你有多少个没有听说过的谷歌产品呢?
- ORA-00911
- 下游Streams流的配置
- 打乱生活的魔方
- MAPGIS-TDE 三维处理平台简介
- 《Effective C#》 Item 26:对需要排序的对象实现IComparable和IComparer接口
- linux下tomcat启动后出现多个java进程
- Windows进程/线程浅谈
- 什么是企业实现知识管理难点?如何解决?
- 收藏《CnForums和已有系统的整合方案》《通过IViewObject接口,取浏览器的图象,实现SNAP 》《SNAP的另类实现,采用iFRAME,内嵌框架的形式.》等
- 求购ERP的信息永远少一条
- 利用递归实现不定重数多重循环
- 学习mvc,hibernate,struts,spring的好书
- callback(2)--使用代理