问题
要使自定义类型能够像一个List<T>或SortedList<K,V>结构那样可排序。一般情况下,我们会倾向于使用List<T>.Sort方法或SortedList<K,V>的内部排序机制,来定制由用户自定义数据类型组成的数组中的排序规则。此外,用户可能需要在SortedList集合中使用此类型。
解决方案
例3-1示范了怎样实现IComparable<T>接口。例3-1中的Square类实现了该接口使其能够像List<T>和SortedList<K,V>集合那样对Square对象进行排序和搜索。
//示例3-1.实现IComparable<T>接口,让类型可以排序 public class Square : IComparable<Square> { public Square() { } public Square(int height, int width) { this.Height = height; this.Width = width; } public int Height { get; set; } public int Width { get; set; } public int CompareTo(object obj) { Square square = obj as Square; if (square != null) return CompareTo(square); throw new ArgumentException("Both objects being compared must be of type Square."); } public override string ToString() { return ("Height:" + this.Height + " Width:" + this.Width); } public override bool Equals(object obj) { if (obj == null) return false; Square square = obj as Square; if (square != null) return this.Height == square.Height; return false; } public override int GetHashCode() { return this.Height.GetHashCode() | this.Width.GetHashCode(); } public static bool operator ==(Square x, Square y) { return x.Equals(y); } public static bool operator !=(Square x, Square y) { return !(x == y); } public static bool operator <(Square x, Square y) { return (x.CompareTo(y) < 0); } public static bool operator >(Square x, Square y) { return (x.CompareTo(y) > 0); } #region IComparable<Square> Members public int CompareTo(Square other) { long area1 = this.Height * this.Width; long area2 = other.Height * other.Width; if (area1 == area2) return 0; else if (area1 > area2) return 1; else if (area1 < area2) return -1; else return -1; } #endregion }
讨论
依靠在自定义类型(或结构)中实现IComparable<T>接口,可以得到List<T>和SortedList<K,V>类一样的排序算法的性能优势。排序算法内置在这些类定义中,用户需要做的就是实现IComparable<T>.CompareTo方法,通过代码告诉他们怎样对用户自定义的类进行排序。
//示例3-2. 实现IComparable<T>接口,让类型可以排序 public class CompareHeight : IComparer<Square> { public int Compare(object firstSquare, object secondSquare) { Square square1 = firstSquare as Square; Square square2 = secondSquare as Square; if (square1 == null || square2 == null) throw (new ArgumentException("Both parameters must be of type Square.")); else return Compare(firstSquare, secondSquare); } #region IComparer<Square> Members public int Compare(Square x, Square y) { if (x.Height == y.Height) return 0; else if (x.Height > y.Height) return 1; else if (x.Height < y.Height) return -1; else return -1; } #endregion }
当调用List<Square>.Sort方法对一个Square对象的列表进行排序时,该列表使用Square对象的IComparable<Square>接口来实现排序。当它们被添加到SortedList<K,V>中时,SortedList<K,V>的Add方法使用该接口对上述对象进行排序。
设计IComparer<T>接口是用于解决在不同的上下文关系(不同的自定义类型)中以不同的标准排序的问题。这些接口也可以使用户对那些没有实现的类排序。如果想按照宽度对Square对象排序,那么要创建一个名叫CompareHeight的新类型,同样也要实现IComparer<Square>接口,如示例3-2所示:
当该类传入Sort方法的IComparer参数时,用户可指定不同的规则对Square对象排序。完成的比较方法必须是一致的和全序的,所以当比较函数比较两个对象是否相等时,当一个对象不大于或不小于另一个对象时,将返回绝对真和假的结果。
为了最好的表现效果,保持CompareTo方法的简洁,因为该方法会被Sort方法调用多次。例如,对一个拥有4个对象的数组排序,Compare方法将被调用10次。 |
示例3-3的TestSort方法演示了在Square类和CompareHeight类中怎样使用List<Square> 和SortedList<int,Square>实例。
//示例3-3. TestSort方法 public static void TestSort() { List<Square> listOfSquares = new List<Square>(){ new Square(1,3), new Square(4,3), new Square(2,1), new Square(6,1)}; // Test a List<String> Console.WriteLine("List<String>"); Console.WriteLine("Original list"); foreach (Square square in listOfSquares) { Console.WriteLine(square.ToString()); } Console.WriteLine(); IComparer<Square> heightCompare = new CompareHeight(); listOfSquares.Sort(heightCompare); Console.WriteLine("Sorted list using IComparer<Square>=heightCompare"); foreach (Square square in listOfSquares) { Console.WriteLine(square.ToString()); } Console.WriteLine(); Console.WriteLine("Sorted list using IComparable<Square>"); listOfSquares.Sort(); foreach (Square square in listOfSquares) { Console.WriteLine(square.ToString()); } // Test a SORTEDLIST var sortedListOfSquares = new SortedList<int, Square>(){ { 0, new Square(1,3)}, { 2, new Square(3,3)}, { 1, new Square(2,1)}, { 3, new Square(6,1)}}; Console.WriteLine(); Console.WriteLine(); Console.WriteLine("SortedList<Square>"); foreach (KeyValuePair<int, Square> kvp in sortedListOfSquares) { Console.WriteLine(kvp.Key + " : " + kvp.Value); } }
上述代码的输出结果如下所示:
List<String>
Original list
Height:1 Width:3
Height:4 Width:3
Height:2 Width:1
Height:6 Width:1
Sorted list using IComparer<Square>=heightCompare
Height:1 Width:3
Height:2 Width:1
Height:4 Width:3
Height:6 Width:1
Sorted list using IComparable<Square>
Height:2 Width:1
Height:1 Width:3
Height:6 Width:1
Height:4 Width:3
SortedList<Square>
0 : Height:1 Width:3
1 : Height:2 Width:1
2 : Height:3 Width:3
3 : Height:6 Width:1
还可参见
条目3.3;还有MSDN文档中的“IComparable<T>接口”主题。