无法调用 Array.BinarySearch 中键的比较器

本文关键字:比较器 BinarySearch 调用 Array | 更新日期: 2023-09-27 18:35:09

为了观察System.Array.BinarySearch中键比较器的调用(与涉及数组元素比较器调用的典型场景相反),我们设置了以下测试:

class Many 
{
    public string data { get; set; }
}
class One : Many, IComparable
{
    public int CompareTo(object arg)
    {
        Many other = arg as Many;
        Console.WriteLine("Comparator of One invoked from: " + this.data);
        if (this.data.Length < other.data.Length) return -1;
        if (this.data.Length > other.data.Length) return 1;
        return 0;
    }
}

当我运行以下内容时,使用这些新声明:

   Many[] manies = new[] { new Many { data = "1" }, 
                           new Many { data = "22" }, 
                           new Many { data = "333" }, 
                           new Many { data = "4444" }, };
   One one = new One {data="???"};
   Console.WriteLine(Array.BinarySearch(manies, one));

我得到作为输出:

Unhandled Exception: System.InvalidOperationException: Failed to compare two elements in the array. ---> System.ArgumentException: At least one object must implement IComparable.
   at System.Collections.Comparer.Compare(Object a, Object b)
   at System.Collections.Generic.ObjectComparer`1.Compare(T x, T y)
   at System.Collections.Generic.ArraySortHelper`1.InternalBinarySearch(T[] array, Int32 index, Int32 length, T value, IComparer`1 comparer)
   at System.Collections.Generic.ArraySortHelper`1.BinarySearch(T[] array, Int32 index, Int32 length, T value, IComparer`1 comparer)
   --- End of inner exception stack trace ---
   at System.Collections.Generic.ArraySortHelper`1.BinarySearch(T[] array, Int32 index, Int32 length, T value, IComparer`1 comparer)
   at System.Array.BinarySearch[T](T[] array, Int32 index, Int32 length, T value, IComparer`1 comparer)
   at System.Array.BinarySearch[T](T[] array, T value)
   at ConsoleApplication1.Prosram.Main(String[] args) in C:'Users'xxx'documents'visual studio 2010'Projects'ConsoleApplication1'ConsoleApplication1'Program.cs:line xxx

无法调用 Array.BinarySearch 中键的比较器

BinarySearch的行为一直存在问题,因为键的比较器从未实际执行过,即使 MSDN 文档说如果数组元素没有实现IComparable就是这种情况。此问题已在 .NET 版本 4.5 中解决。

错误的堆栈跟踪中有线索显示源自 System.Collections.Comparer.Compare() 的错误。在 .NET 4.5 和 .NET 4.0 中查看此方法的 .NET 源代码,可以看到此方法的实现方式发生了重要变化:

.NET 4.5:

public int Compare(Object a, Object b) {
    if (a == b) return 0;
    ...
    IComparable ia = a as IComparable; 
    if (ia != null)
        return ia.CompareTo(b); 
    IComparable ib = b as IComparable;
    if (ib != null) 
        return -ib.CompareTo(a);
    throw new ArgumentException(Environment.GetResourceString(
                                       "Argument_ImplementIComparable"));

.NET 4.0:

public int Compare(Object a, Object b) {
    if (a == b) return 0;
    ...
    IComparable ia = a as IComparable; 
    if (ia != null)
        return ia.CompareTo(b); 
    throw new ArgumentException(Environment.GetResourceString(
                                      "Argument_ImplementIComparable"));

在我们的例子中,我们要观察正在调用键的比较器,并且在这种情况下,键是b的,而数组中的元素是a的。

这意味着 BinarySearch 方法的 MSDN 文档对于 4.5 之前的 .NET 版本不正确。如果数组中的元素没有实现IComparable,该方法不会考虑键的Comparator,因为实现取决于实际执行此操作Comparer并且它不会这样做,而是抛出ArgumentException。但是,在 4.5 版中,它确实像 BinarySearch 中记录的那样工作。