2010-07-02 86 views
15

我需要在内存中按升序或降序排序字符串或数字。但是,列表可以包含空值,并且所有空值必须出现在数字或字符串后面。如何在LINQ中进行自定义排序,并始终使用null?

即输入数据可能是:

1, 100, null, 5, 32.3

上升的结果将是

1, 5, 32.3, 100, null

的下降清单将是

100, 32.3, 5, 1, null

如何使这个任何想法工作?

回答

6

你可以编写你自己的比较器,它代表一个现有的比较器,用于非空值,但总是在最后排序空值。事情是这样的:

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排序实现,实际上依赖于这一点,但它绝对值得意识到的

+0

这不是对.NET排序实现的违约吗?我认为我已经阅读过某些地方,排序方法可以假定null先来...或者...? – 2010-07-02 06:33:40

+0

@Lasse:这是对'IComparer '界面的违反,是的 - 我会编辑我的帖子来提到这一点。 – 2010-07-02 06:35:12

+0

是的,但是从我从记忆中得知的情况来看,这听起来像是由于这个契约,排序方法可能带有捷径,因此在某些情况下实际上不会调用比较方法,因为它“已经知道”结果是什么将。换句话说,在某些情况下,这不会产生时髦的结果吗? – 2010-07-02 06:36:26

38

我没有在我面前一个编译器检查,但我想是这样的:。

x.OrderBy(i => i == null).ThenBy(i => i) 
+3

尼斯...这是一个非常可爱的方法。如果我可以的话,我会多次投票... – 2010-07-02 06:38:53

+0

非常好。这种方法也适用于Linq-To-Entities并将其正确转换为Sql。 – 2015-05-06 17:01:05

0

正如乔恩说,你需要定义您的自定义比较器,实施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); 
    } 
相关问题