首页 > 解决方案 > 使用 IComparer 基于多个字段进行排序

问题描述

我使用下面的代码对List<DataAccessViewModel>列表进行排序。

这是排序顺序:

  1. 优先分数
  2. 名称
  3. 名称
  4. 名称

它按预期工作。

public int Compare(DataAccessViewModel x, DataAccessViewModel y)
{
    if (x == null || y == null)
    {
        return 0;
    }

    return x.CompareTo(y);
}

public int CompareTo(DataAccessViewModel mod)
{
    int retval = (int)(this.PriorityScore?.CompareTo(mod.PriorityScore));
    if(retval != 0)
        return retval;
    else
    {
        retval = (this.MName ?? "zzzzzzzzzzzzz").CompareTo(mod.MName ?? "zzzzzzzzzzzzz");
        if (retval != 0)
            return retval;
        else
        {
            retval = (this.CName ?? "zzzzzzzzzzzzz").CompareTo(this.CName ?? "zzzzzzzzzzzzz");
            if (retval != 0)
                return retval;
            else
                retval = (this.FName ?? "zzzzzzzzzzzzz").CompareTo(this.FName ?? "zzzzzzzzzzzzz");
        }
    }
        
    return retval;
}

但是代码对我来说看起来很笨拙。有没有更好的方法或者是这样吗?

标签: c#sortingmodel-view-controllericomparer

解决方案


当前代码存在问题:

public int Compare(DataAccessViewModel x, DataAccessViewModel y) 
{
    if (x == null || y == null)
    {
        return 0;
    }
    ...

因为null等于任何值,所以所有值都相等

   a == null, null == b => a == b

这不是您要实施的规则。我建议这样的事情

using System.Linq;

...

// static: we don't want "this"
public static int TheCompare(DataAccessViewModel x, DataAccessViewModel y) {
  // Special cases, nulls
  if (ReferenceEquals(x, y)) // if references are shared, then equal
    return 0;
  if (null == x) // let null be smaller than any other value
    return -1;
  if (null == y)
    return 1;
 
  // How we compare strings 
  static int MyCompare(string left, string right) =>
      ReferenceEquals(left, right) ? 0 
    : null == left ? 1    // null is greater than any other string
    : null == right ? -1
    : string.Compare(left, right);

  // Func<int> for lazy computation
  return new Func<int>[] {
      () => x.PriorityScore?.CompareTo(y.PriorityScore) ?? -1,
      () => MyCompare(x.MName, y.MName),
      () => MyCompare(x.CName, y.CName),  
      () => MyCompare(x.FName, y.FName), 
    }
    .Select(func => func())
    .FirstOrDefault(value => value != 0);
}

然后对于接口我们有

public int CompareTo(DataAccessViewModel other) => TheCompare(this, other);

// I doubt if you want to implement both IComparable<T> and
// IComparer<T> but you can easily do it
public int Compare(DataAccessViewModel x, DataAccessViewModel y) =>
  TheCompare(x, y);

推荐阅读