我使用内置的C#List.Sort函数和自定义比较器得到奇怪的行为.
出于某种原因,它有时会将comparer类的Compare方法与null对象作为参数之一进行调用.但是,如果我使用调试器检查列表,则集合中没有空对象.
我的比较器类看起来像这样:
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;
}
}
Run Code Online (Sandbox Code Playgroud)
这允许委托传递给List.Sort方法,如下所示:
mylist.Sort(new DelegateToComparer<MyClass>(
(x, y) => {
return x.SomeProp.CompareTo(y.SomeProp);
});
Run Code Online (Sandbox Code Playgroud)
因此,即使没有mylist的元素为null,上面的委托也会为x参数抛出一个空引用异常.
更新:是的我绝对相信它是参数x抛出空引用异常!
更新:我没有使用框架的List.Sort方法,而是尝试了一种自定义排序方法(即新的BubbleSort().排序(mylist)),问题就消失了.正如我所怀疑的,List.Sort方法由于某种原因将null传递给比较器.
Lee*_*Lee 23
当比较函数不一致时会发生此问题,因此x <y并不总是暗示y <x.在您的示例中,您应该检查如何比较SomeProp类型的两个实例.
这是一个重现问题的例子.这里,它是由病理比较功能"compareStrings"引起的.它取决于列表的初始状态:如果将初始顺序更改为"C","B","A",则没有异常.
我不会将此称为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);
}
}
Run Code Online (Sandbox Code Playgroud)
你确定问题不是这样SomeProp吗null?
特别是对于字符串或Nullable<T>值。
对于字符串,最好使用:
list.Sort((x, y) => string.Compare(x.SomeProp, y.SomeProp));
Run Code Online (Sandbox Code Playgroud)
(编辑)
对于空安全包装器,您可以使用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);
}
}
Run Code Online (Sandbox Code Playgroud)
小智 5
我也遇到过这个问题(空引用被传递给我的自定义 IComparer 实现),最后发现问题是由于使用了不一致的比较函数。
这是我最初的 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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
这段代码中的错误是,当无法正确解析其中一个值时,Compare 将返回 -1(在我的情况下,这是由于数值的字符串表示格式错误,因此 TryParse 总是失败)。
请注意,如果 x 和 y 的格式都不正确(因此 TryParse 对它们都失败了),则调用 Compare(x, y) 和 Compare(y, x) 将产生相同的结果:-1。我认为这是主要问题。调试时,即使正在排序的集合不包含空字符串,Compare() 也会在某个时候将空字符串指针作为其参数之一传递。
一旦我修复了 TryParse 问题并确保我的实现的一致性,问题就消失了,并且 Compare 不再被传递空指针。