Icomparable<T> 和 Icomparaer<T> 实现顺序关系
你的类型应该有一个顺序关系,以便在集合中描述它们如何存储以及排 序。.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" );