简体   繁体   English

C#中的List.Sort:使用null对象调用comparer

[英]List.Sort in C#: comparer being called with null object

I am getting strange behaviour using the built-in C# List.Sort function with a custom comparer. 我使用内置的C#List.Sort函数和自定义比较器得到奇怪的行为。

For some reason it sometimes calls the comparer class's Compare method with a null object as one of the parameters. 出于某种原因,它有时会将comparer类的Compare方法与null对象作为参数之一进行调用。 But if I check the list with the debugger there are no null objects in the collection. 但是,如果我使用调试器检查列表,则集合中没有空对象。

My comparer class looks like this: 我的比较器类看起来像这样:

public class DelegateToComparer<T> : IComparer<T>
{
    private readonly Func<T,T,int> _comparer;

    public int Compare(T x, T y)
    {
        return _comparer(x, y);
    }

    public DelegateToComparer(Func<T, T, int> comparer)
    {
        _comparer = comparer;
    }
}

This allows a delegate to be passed to the List.Sort method, like this: 这允许委托传递给List.Sort方法,如下所示:

mylist.Sort(new DelegateToComparer<MyClass>(
    (x, y) => { 
         return x.SomeProp.CompareTo(y.SomeProp); 
     });

So the above delegate will throw a null reference exception for the x parameter, even though no elements of mylist are null. 因此,即使没有mylist的元素为null,上面的委托也会为x参数抛出一个空引用异常。

UPDATE: Yes I am absolutely sure that it is parameter x throwing the null reference exception! 更新:是的我绝对相信它是参数x抛出空引用异常!

UPDATE: Instead of using the framework's List.Sort method, I tried a custom sort method (ie new BubbleSort().Sort(mylist) ) and the problem went away. 更新:我没有使用框架的List.Sort方法,而是尝试了一种自定义排序方法(即新的BubbleSort()。排序(mylist) ),问题就消失了。 As I suspected, the List.Sort method passes null to the comparer for some reason. 正如我所怀疑的,List.Sort方法由于某种原因将null传递给比较器。

This problem will occur when the comparison function is not consistent, such that x < y does not always imply y < x. 当比较函数不一致时会发生此问题,因此x <y并不总是暗示y <x。 In your example, you should check how two instances of the type of SomeProp are being compared. 在您的示例中,您应该检查如何比较SomeProp类型的两个实例。

Here's an example that reproduces the problem. 这是一个重现问题的例子。 Here, it's caused by the pathological compare function "compareStrings". 这里,它是由病理比较功能“compareStrings”引起的。 It's dependent on the initial state of the list: if you change the initial order to "C","B","A", then there is no exception. 它取决于列表的初始状态:如果将初始顺序更改为“C”,“B”,“A”,则没有异常。

I wouldn't call this a bug in the Sort function - it's simply a requirement that the comparison function is consistent. 我不会将此称为Sort函数中的错误 - 它只是比较函数一致的要求。

using System.Collections.Generic;

class Program
{
    static void Main()
    {
        var letters = new List<string>{"B","C","A"};

        letters.Sort(CompareStrings);
    }

    private static int CompareStrings(string l, string r)
    {
        if (l == "B")
            return -1;

        return l.CompareTo(r);
    }
}

Are you sure the problem isn't that SomeProp is null ? 你确定问题不是SomePropnull吗?

In particular, with strings or Nullable<T> values. 特别是,使用字符串或Nullable<T>值。

With strings, it would be better to use: 使用字符串,最好使用:

list.Sort((x, y) => string.Compare(x.SomeProp, y.SomeProp));

(edit) (编辑)

For a null-safe wrapper, you can use Comparer<T>.Default - for example, to sort a list by a property: 对于null安全包装器,您可以使用Comparer<T>.Default - 例如,按属性对列表进行排序:

using System;
using System.Collections.Generic;
public static class ListExt {
    public static void Sort<TSource, TValue>(
            this List<TSource> list,
            Func<TSource, TValue> selector) {
        if (list == null) throw new ArgumentNullException("list");
        if (selector == null) throw new ArgumentNullException("selector");
        var comparer = Comparer<TValue>.Default;
        list.Sort((x,y) => comparer.Compare(selector(x), selector(y)));
    }
}
class SomeType {
    public override string ToString() { return SomeProp; }
    public string SomeProp { get; set; }
    static void Main() {
        var list = new List<SomeType> {
            new SomeType { SomeProp = "def"},
            new SomeType { SomeProp = null},
            new SomeType { SomeProp = "abc"},
            new SomeType { SomeProp = "ghi"},
        };
        list.Sort(x => x.SomeProp);
        list.ForEach(Console.WriteLine);
    }
}

I too have come across this problem (null reference being passed to my custom IComparer implementation) and finally found out that the problem was due to using inconsistent comparison function. 我也遇到过这个问题(将null引用传递给我的自定义IComparer实现),最后发现问题是由于使用了不一致的比较函数。

This was my initial IComparer implementation: 这是我最初的IComparer实现:

public class NumericStringComparer : IComparer<String>
{
    public int Compare(string x, string y)
    {
        float xNumber, yNumber;
        if (!float.TryParse(x, out xNumber))
        {
            return -1;
        }
        if (!float.TryParse(y, out yNumber))
        {
            return -1;
        }
        if (xNumber == yNumber)
        {
            return 0;
        }
        else
        {
            return (xNumber > yNumber) ? 1 : -1;
        }
    }
}

The mistake in this code was that Compare would return -1 whenever one of the values could not be parsed properly (in my case it was due to wrongly formatted string representations of numeric values so TryParse always failed). 这段代码中的错误是,只要其中一个值无法正确解析,Compare就会返回-1(在我的情况下,这是由于数值的错误格式化字符串表示,因此TryParse总是失败)。

Notice that in case both x and y were formatted incorrectly (and thus TryParse failed on both of them), calling Compare(x, y) and Compare(y, x) would yield the same result: -1. 请注意,如果x和y的格式都不正确(因此TryParse在两者上都失败了),调用Compare(x,y)和Compare(y,x)会产生相同的结果:-1。 This I think was the main problem. 我认为这是主要问题。 When debugging, Compare() would be passed null string pointer as one of its arguments at some point even though the collection being sorted did not cotain a null string. 在调试时,即使正在排序的集合没有包含空字符串,Compare()也会在某些时候将null字符串指针作为其参数之一传递。

As soon as I had fixed the TryParse issue and ensured consistency of my implementation the problem went away and Compare wasn't being passed null pointers anymore. 一旦我修复了TryParse问题并确保了我的实现的一致性,问题就消失了,并且Compare不再被传递空指针。

Marc's answer is useful. 马克的回答很有用。 I agree with him that the NullReference is due to calling CompareTo on a null property. 我同意他的说法,NullReference是由于在null属性上调用CompareTo。 Without needing an extension class, you can do: 无需扩展类,您可以:

mylist.Sort((x, y) => 
      (Comparer<SomePropType>.Default.Compare(x.SomeProp, y.SomeProp)));

where SomePropType is the type of SomeProp 其中SomePropType是SomeProp的类型

For debugging purposes, you want your method to be null-safe. 出于调试目的,您希望您的方法是null安全的。 (or at least, catch the null-ref. exception, and handle it in some hard-coded way). (或者至少,捕获null-ref。异常,并以一些硬编码的方式处理它)。 Then, use the debugger to watch what other values get compared, in what order, and which calls succeed or fail. 然后,使用调试器来查看其他值的比较,按什么顺序,以及哪些调用成功或失败。

Then you will find your answer, and you can then remove the null-safety. 然后你会找到你的答案,然后你可以删除null安全。

Can you run this code ... 你能运行这段代码吗?

mylst.Sort((i, j) =>
              {
                  Debug.Assert(i.SomeProp != null && j.SomeProp != null);
                  return i.SomeProp.CompareTo(j.SomeProp);
              }
         );

I stumbled across this issue myself, and found that it was related to a NaN property in my input. 我自己偶然发现了这个问题,发现它与我输入的NaN属性有关。 Here's a minimal test case that should produce the exception: 这是一个应该产生异常的最小测试用例:

public class C {

    double v;

    public static void Main() {
        var test =
            new List<C> { new C { v = 0d },
                          new C { v = Double.NaN },
                          new C { v = 1d } };
        test.Sort((d1, d2) => (int)(d1.v - d2.v));
    }

}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 List.Sort()如何使用提供的Comparer实例? - How List.Sort() uses provided instance of Comparer? C#list.Sort和CompareTo抛出IndexOutOfRangeException - C# list.Sort and CompareTo throw IndexOutOfRangeException DirectorySearcher SizeLimit和List.Sort产生不必要的结果C# - DirectorySearcher SizeLimit and List.Sort producing unwanted results C# 为什么我在C#中的List.Sort方法颠倒了我的列表顺序? - Why is my List.Sort method in C# reversing the order of my list? 使用 List.Sort(Comparison) 在 C# 中对列表进行排序<T>比较 - Sorting a List in C# using List.Sort(Comparison<T> comparison List.Sort()引发mscorlib.dll中发生类型为&#39;System.InvalidOperationException&#39;的未处理异常-C# - List.Sort() throws An unhandled exception of type 'System.InvalidOperationException' occurred in mscorlib.dll - C# C# 使用比较器排序给出的结果不正确 - C# sort with Comparer is giving incorrect result 在C#中使用NumericComparer代码,为什么我的List.Sort()收到转换错误? - Using NumericComparer code in C#, why I am getting a conversion error for my List.Sort()? list.Sort ArgumentException错误:IComparer不返回0(null) - list.Sort ArgumentException error: IComparer doesn't not return 0(null) C#使用比较器进行二进制搜索的对象列表 - C# List of objects binary search with a comparer
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM