我需要在内存中按升序或降序排序字符串或数字。但是,列表可以包含空值,并且所有空值必须出现在数字或字符串后面。如何在LINQ中进行自定义排序,并始终使用null?
即输入数据可能是:
1, 100, null, 5, 32.3
上升的结果将是
1, 5, 32.3, 100, null
的下降清单将是
100, 32.3, 5, 1, null
如何使这个任何想法工作?
我需要在内存中按升序或降序排序字符串或数字。但是,列表可以包含空值,并且所有空值必须出现在数字或字符串后面。如何在LINQ中进行自定义排序,并始终使用null?
即输入数据可能是:
1, 100, null, 5, 32.3
上升的结果将是
1, 5, 32.3, 100, null
的下降清单将是
100, 32.3, 5, 1, null
如何使这个任何想法工作?
你可以编写你自己的比较器,它代表一个现有的比较器,用于非空值,但总是在最后排序空值。事情是这样的:
public class NullsLastComparer<T> : IComparer<T>
{
private readonly IComparer<T> proxy;
public NullsLastComparer(IComparer<T> proxy)
{
this.proxy = proxy;
}
public override int Compare(T first, T second)
{
if (first == null && second == null)
{
return 0;
}
if (first == null)
{
return 1;
}
if (second == null)
{
return -1;
}
return proxy.Compare(first, second);
}
}
编辑:这种方法的几个问题:
首先,它不能很好地与匿名类型游戏;您可能需要一个单独的扩展方法来使其工作正常。或者使用肯的回答:)
更重要的是,它违反了IComparer<T>
合同,该合同规定空位应该是第一位。现在我个人认为这是IComparer<T>
规范中的一个错误 - 它也许应该定义为处理空值,但它应该而不是指定它们是先来还是后来......它会提出这样的要求是完全合理的)不可能像我们想要的那样干净地完成,并且对于倒转比较器之类的东西具有各种尴尬的后果。你会期望这样的事情完全颠倒顺序,但根据规范,它应该仍然在开始时保持空值:(
我不认为我见过任何.NET排序实现,实际上依赖于这一点,但它绝对值得意识到的
我没有在我面前一个编译器检查,但我想是这样的:。
x.OrderBy(i => i == null).ThenBy(i => i)
尼斯...这是一个非常可爱的方法。如果我可以的话,我会多次投票... – 2010-07-02 06:38:53
非常好。这种方法也适用于Linq-To-Entities并将其正确转换为Sql。 – 2015-05-06 17:01:05
正如乔恩说,你需要定义您的自定义比较器,实施IComparer
。以下是您的自定义比较器中的Compare
方法可以保持的方式null
最后。
public int Compare(Object x, Object y)
{
int retVal = 0;
IComparable valX = x as IComparable;
IComparable valY = y as IComparable;
if (valX == null && valY == null)
{
return 0;
}
if (valX == null)
{
return 1;
}
else if (valY == null)
{
return -1;
}
return valX.CompareTo(valY);
}
这不是对.NET排序实现的违约吗?我认为我已经阅读过某些地方,排序方法可以假定null先来...或者...? – 2010-07-02 06:33:40
@Lasse:这是对'IComparer'界面的违反,是的 - 我会编辑我的帖子来提到这一点。 –
2010-07-02 06:35:12
是的,但是从我从记忆中得知的情况来看,这听起来像是由于这个契约,排序方法可能带有捷径,因此在某些情况下实际上不会调用比较方法,因为它“已经知道”结果是什么将。换句话说,在某些情况下,这不会产生时髦的结果吗? – 2010-07-02 06:36:26