user11224591
user11224591

Reputation:

mutiple sorting rule with IComparable and IComparer?

I'm new to C#, just a question on how to use IComparable and IComparer properly. Let's say we have the following class:

public class Student
{
    int score;
    string name;
}

and I want to sort by socre (desc) first then sort by name (asc).

Now assume that I can't access to the Student class, so I can only use IComparer so I have to make one help class(let's say it is called StudentComparer) and put the same logic into


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
   }
}

Here is my question, if I just need to use a single rule later, for example, sometimes just sort by name and sometimes just sort by score. So I have to make another two help classes(ScoreComparer and NameComparer) that implement IComparer, which have duplicated code in StudentComparer as

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
   }
}

my case is pretty simple, image that if there is a complicated case, each comparer consist of hundreds line of code,so how can I avoid duplicated code? or is it a way to combine multiple Comparer A, B, C, D ... into a common Comparer so that it check A,B,C,D in sequence, just like order by clause in SQL

Upvotes: 0

Views: 129

Answers (1)

Zohar Peled
Zohar Peled

Reputation: 82474

Back in the days before linq, I used to use comparers a lot.
The way I've handled different sort options was by specifying these options in the constructor of the comparer implementation and then use that information in the Compare method.

Lucky for us, we now have linq so this entire thing can be done with a single, fluent line of code:

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

However, if for some reason you need to work the old-fashion way, using comparers and stuff like that, here is how I would handle this back then:

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
   }
}

Upvotes: 0

Related Questions