Effective C#原则26:用IComparable和IComparer实现对象的顺序关系

你的类型应该有一个顺序关系,以便在集合中描述它们如何存储以及排 序。.Net框架为你提供了两个接口来描述对象的顺序关系:IComparable 和 IComparer。IComparable 为你的类定义了自然顺序,而实现IComparer接口的类 可以描述其它可选的顺序。你可以在实现接口时,定义并实现你自己关系操作符 (<,>,<=,>=),用于避免在运行时默认比较关系的低效问题。这 一原则将讨论如何实现顺序关系,以便.Net框架的核心可以通过你定义的接口对 你的类型进行排序。这样用户可以在些操作上得更好的效率。

IComparable接口只有一个方法:CompareTo(),这个方法沿用了传统的C 函数库里的strcmp函数的实现原则:如果当前对象比目标对象小,它的返回值小 于0;如果相等就返回0;如果当前对象比目标对象大,返回值就大于0。 IComparable以System.Object做为参数,因此在使用这个函数时,你须要对运行 时的对象进行检测。每次进行比较时,你必须重新解释参数的类型:

public struct Customer : IComparable
{
  private readonly string _name;
 public Customer( string name )
 {
  _name = name;
 }
 #region IComparable Members
 public int 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
}

关于实现比较与IComparable接口的一致性有 很多不太喜欢的地方,首先就是你要检测参数的运行时类型。不正确的代码可以 用任何类型做为参数来调用CompareTo方法。还有,正确的参数还必须进行装箱 与拆箱后才能提供实际的比较。每次比较都要进行这样额外的开销。在对集合进 行排序时,在对象上进行的平均比较次数为N x log(N),而每次都会产生三次装 箱与拆箱。对于一个有1000个点的数组来说,这将会产生大概20000次的装箱与 拆箱操作,平均计算:N x log(n) 有7000次,每次比较有3次装箱与拆箱。因此 ,你必须自己找个可选的比较方法。你无法改变IComparable.CompareTo()的定 义,但这并不意味着你要被迫让你的用户在一个弱类型的实现上也要忍受性能的 损失。你可以重载CompareTo()方法,让它只对Customer 对象操作:

public struct Customer : IComparable
{
  private string _name;
 public Customer( string name )
  {
  _name = name;
 }
 #region IComparable Members
 // IComparable.CompareTo()
 // This is not type safe. The runtime type
 // of the right parameter must be checked.
 int IComparable.CompareTo( object right )
 {
  if ( ! ( right is Customer ) )
   throw new ArgumentException( "Argument not a customer",
     "right" );
  Customer rightCustomer = ( Customer ) right;
  return CompareTo( rightCustomer );
 }
 // type-safe CompareTo.
 // Right is a customer, or derived from Customer.
 public int CompareTo( Customer right )
 {
  return _name.CompareTo( right._name );
 }
  #endregion
}

现在,IComparable.CompareTo()就是一个隐 式的接口实现,它只能通过IComparable 接口的引用才能调用。你的用户则只能 使用一个类型安全的调用,而且不安全的比较是不可能访问的。下面这样无意的 错误就不能通过编译了:

Customer c1;
Employee e1;
if ( c1.CompareTo( e1 ) > 0 )
 Console.WriteLine( "Customer one is greater" );

时间: 2024-10-27 17:39:22

Effective C#原则26:用IComparable和IComparer实现对象的顺序关系的相关文章

Effective C#原则50:了解ECMA标准

ECMA标准是C#语言所有功能的官方说明.ECMA-334定义了C#语言1.0的标准, 你可以从The C# Programming Language这本书上学习C#2.0的计划(译注:现在 已经不是计划了),这本书的作者是Anders Hejlsberg, Scott Wiltamuth, 和 Peter Golde (Addison-Wesley, 2003).这本书是一个语言手册,而不是指南. 它详细说明了这门语言书面定义的每一个功能.每一种语言都只一种标记,可以 让你更加明白每一种语言的

Effective C#原则48:了解更多的工具和资源

对于C#以及.Net来说这是激动人心的时候.这些工具目前还是比较新的,整 个社区都在学习如何使用这些工具.一些资源可以帮助你提高你的知识,以及为 .Net和C#创建一个更大的知识社区.这些工具是我每天都向C#开发人员推荐的. 关于C#实践的全部内容还在写作当中,跟进它们而且不断了解相关的内容. 第一个应该在每一个C#开发人员的工具箱的工具是NUnit, 它可以在 www.nunit.org网站上找到.NUnit是一个自动进行单元测试的工具,功能和 JUnit很像.和其它大多数开发人员一样,我讨厌

Effective C#原则47:选择安全的代码

.Net运行时已经设计好了,一些怀有恶意的代码不能渗透到远程计算机上并 执行.目前一些分部式系统依懒于从远程机器上下载和执行代码.如果你可以通 过Internet或者以太网来发布你的软件,或者直接从web上运行,但你须要明白 CRL在你的程序集中的一些限制.如果CLR不是完全相信一个程序集,它会限制一 些的行为.这些调用代码要有访问安全认证(CAS).从另一方面来说,CLR强制要 求基于角色的安全认证,这样这些代码才能或者不能在基于一个特殊的角色帐号 下运行. 安全违例是运行时条件,编译器不能强

Effective C#原则45:选择强异常来保护程序

当你抛出异常时,你就在应用程序中引入了一个中断事件.而且危机到程序 的控制流程.使得期望的行为不能发生.更糟糕的是,你还要把清理工作留给最 终写代码捕获了异常的程序员.而当一个异常发生时,如果你可以从你所管理的 程序状态中直接捕获,那么你还可以采取一些有效的方法.谢天谢地,C#社区不 须要创建自己的异常安全策略,C++社区里的人已经为我们完成了所有的艰巨的 工作.以Tom Cargill的文章开头:"异常处理:一种错误的安全感觉, " 而且Herb Sutter,Scott Meyer

Effective C#原则43:请勿滥用反射

创建二进制的组件时,同时也意味着你要使用迟后绑定和反射来查找你所须 要的具有特殊功能代码.反射是一个很有力的工具,而且它让你可以写出可动态 配置的软件.使用反射,一个应用程序可以通过添加新的组件来更新功能,而这 些组件是在软件最开始发布时没有的.这是有利的. 这一伸缩性也带来 了一些复杂的问题,而且复杂问题的增加又会增加出现其它问题的可能.当你使 用反射时,你是围绕着C#的安全类型.然而,成员调用的参数和返回值是以 System.Object类型存在的.你必须在运行时确保这些类型是正确的.简单的

Effective C#原则42:使用特性进行简单的反射

当你创建了一个与反射相关的系统时,你应该为你自己的类型,方法,以及 属性定义一些自己的特性,这样可以让它们更容易的被访问.自定义的特性标示 了你想让这些方法在运行时如何被使用.特性可以测试一些目标对象上的属性. 测试这些属性可以最小化因为反射时可能而产生的类型错误. 假设你须 要创建一个机制,用于在运行时的软件上添加一个菜单条目到一个命令句柄上.这个须要很简单:放一个程序集到目录里,然后程序可以自己发现关于它的一些 新菜单条目以及新的菜单命令.这是利用反射可以完成的最好的工作之一:你的 主程序须

Effective C#原则41:选择DataSet而不是自定义的数据结构

因为两个原则,把DataSet的名声搞的不好.首先就是使用XML序列化的 DataSet与其它的非.Net代码进行交互时不方便.如果在Web服务的API中使用 DataSet时,在与其它没有使用.Net框架的系统进行交互时会相当困难.其次, 它是一个很一般的容器.你可以通过欺骗.Net框架里的一些安全类型来错误 DataSet.但在现代软件系统中,DataSet还可以解决很多常规的问题.如果你明 白它的优势,避免它的缺点,你就可以扩展这个类型了. DataSet类设计 出来是为了离线使用一些存储

Effective C#原则40:根据需求选择集合

"哪种集合是最好的?"答案是:"视情况而定." 不同的集合有不同的性能,而且在不同的行为上有不同的优化..Net框架支持很 多类似的集合:链表,数组,队列,栈,以及其它的一些集合.C#支持多维的数 组,它的性能与一维的数组和锯齿数组都有所不同..Net框架同样包含了很多特 殊的集合,在你创建你自己的集合类之前,请仔细参阅这些集合.你可以发现很 多集合很快,因为所有的集合都实现了ICollection接口.在说明文档中列出了 所有实现了ICollection接口的集合

Effective C#原则37:使用标准的配置机制

我们要寻求一种避免直接写代码的应用程序配置和信息设置方法,我们已经 创建了多种不同的策略来存储配置信息.而我们是要寻求一种正确的方法,我们 要不断提高和改我们的想法,关于哪里是放置这些信息的好地方.INI文件?这 是Windows3.1做的事,配置信息的结构是受限制的,而且在文件名上可能还会与 其它程序程序相冲突.注册表?是的,是这个正确的想法,但它也有它的限制. 乱七八糟的程序可能会通过在注册表里写一些错误信息来严重破坏计算机.正因 为写注册表存在危险,一个应用程序必须有管理员权限来写注册表的