首页 > 解决方案 > 使用 IComparable 和 IComparer 的多重排序规则?

问题描述

我是 C# 新手,只是关于如何正确使用 IComparable 和 IComparer 的问题。假设我们有以下类:

public class Student
{
    int score;
    string name;
}

我想先按 socre (desc) 排序,然后按 name (asc) 排序。

现在假设我无法访问 Student 类,所以我只能使用 IComparer 所以我必须创建一个帮助类(假设它称为 StudentComparer)并将相同的逻辑放入


public class StudentComparer: IComparer
{
   int Compare(object o1, object o2)
   {
      Student s1 = o1 as Student;
      Student s2 = o2 as Student;
      // not checking null for simplicity
      if (s1.score == s2.score)
         return String.Compare(s1.name, s2.name);
      else if (s1.score < s2.score)
           return -1
      else
      return 1
   }
}

这是我的问题,如果我以后只需要使用单个规则,例如,有时只按名称排序,有时只按分数排序。所以我必须创建另外两个实现 IComparer 的帮助类(ScoreComparer 和 NameComparer),它们在 StudentComparer 中有重复的代码为

public class ScoreComparer : IComparer
{
   int Compare(object o1, object o2)
   {
      //half logic of StudentComparer
   }
}

public class NameComparer: IComparer
{
   int Compare(object o1, object o2)
   {
      //half logic of StudentComparer
   }
}

我的案例很简单,想象一下如果有一个复杂的案例,每个比较器都包含数百行代码,那么如何避免重复代码?还是一种将多个比较器 A、B、C、D ... 组合成一个通用比较器的方法,以便它按顺序检查 A、B、C、D,就像 SQL 中的 order by 子句一样

标签: c#oop

解决方案


回到 linq 之前的日子,我经常使用比较器。
我处理不同排序选项的方法是在比较器实现的构造函数中指定这些选项,然后在Compare方法中使用该信息。

幸运的是,我们现在有了 linq,所以整个事情可以用一行流畅的代码来完成:

// sortedStudents is an IEnumerable<Student> sorted by score and name.
var sortedStudents = students.OrderBy(s => s.Score).ThenBy(s => s.Name);

但是,如果由于某种原因您需要使用老式的方式,使用比较器和类似的东西,那么我将如何处理这个问题:

internal enum CompareBy
{
    NameOnly,
    ScoreAndName
}

public class StudentComparer: IComparer<Student>
{

    private CompareBy _compareBy
    public StudentComparer(CompareBy compareBy)
    {
        _compareBy = compareBy;
    }

    public int Compare(Student s1, Student s2)
    {
        // not checking null for simplicity

        var nameCompare = string.Compare(s1.name, s2.name);
        if(_compareBy == NameOnly)
        {
            return nameCompare;
        }
        // since there are only two members in the enum it's safe to write it like this.
        // if the enum grows, you must change the code.
        if (s1.score == s2.score)
        {
            return nameCompare;
        }
        else if (s1.score < s2.score)
        {
            return -1
        }
        return 1
   }
}

推荐阅读