Article of the Month:

Custom Comparisons Using C#
Archive of
Previous Articles


In numeric comparisons, it is intuitively clear whether number is less than or
greater than a second number. If we consider string comparisons, however, an
element of ambiguity arises: assuming that we are comparing characters from
the same character set, determining whether one string precedes or follows a
second string in alphabetical order (and hence is "less than" or "greater than"
the second string) depends on whether the comparison is case-sensitive or
case-insensitive.

In .NET, when array or collection objects are sorted or searched, the
comparison by default is case-sensitive. For example, the following C# code
creates an array of book titles and sorts them:





















After the program sorts the elements of the array, it outputs them in the
following order:

Adventures of Sherlock Holmes, The
Call of the Wild, The
Dead Souls
Hound of the Baskervilles, The
What Is to Be Done?
What Is To Be Done?
White Fang

Notice that "What Is to Be Done?" precedes "What Is To Be Done?", even
though the latter title was saved to the array first. This reflects the fact that the
in sorting the array, .NET has used a case-sensitive comparison.

Although it isn't evident from this code, the method of comnparison is defined
by a class that implements the IComparer interface. If no instance of an
implementing class is provided as a parameter to the
Array.Sort method,
.NET uses the default
Comparer class. This means that we can duplicate
the previous output if we explicitly indicate that the
Comparer class should






















The result is identical to the output from our first program:

Adventures of Sherlock Holmes, The
Call of the Wild, The
Dead Souls
Hound of the Baskervilles, The
What Is to Be Done?
What Is To Be Done?
White Fang

We can change the sort order by supplying an instance of some other class that
implements
IComparer as the second argument to the Array.Sort
method. The .NET  Framework Class Library provides one other class,
CaseInsensitiveComparer, that allows us to do this. Performing a
case-insensitive sort of our title array requires only slight modification of our
code:
























Since
CaseInsensitiveComparer reorders elements that it considers
to be equal by placing the second element in the comparison before the first,
the result of sorting our original seven-element array would be identical to that
produced by the case-sensitive sort. The difference becomes clearer, however,
if we add a new, all-uppercase title to the array. In that case, the output clearly
shows that the sort has ignored case:

Adventures of Sherlock Holmes, The
Call of the Wild, The
Dead Souls
Hound of the Baskervilles, The
WHAT IS TO BE DONE?
What Is To Be Done?
What Is to Be Done?
White Fang

When we compare objects, though, the basis for the comparison is not quite so
clear. Most tests for "equality" of objects determine whether two object
variables reference the same object. But what if that's not what we're interested
in? Perhaps we'd like to compare objects based on the value of some property
or even a combination of properties, and order the array accordingly. For
example, imagine that we were to modify our simple title application to
implement a Book class that contains author and title information. We'd then
like to sort our array primarily by author and secondarily by title (in other
words, by title within author).

Fortunately, .NET makes these kinds of custom comparisons very easy. All we
have to do define a class that implements the
IComparer interface. This
requires that we implement a single method,
Compare, which has two
objects,
x and y, as parameters and returns an int that indicates the
relative position of
x and y. If x is "greater" than y, the method returns a
positive integer. If
x is "less" than y, the method returns a negative integer. And
if
x is equal to y, the method returns 0.

The following example replaces our array of strings with an array of
Book
objects. The
Book class has two public properties, Author and Title.
We want to be able to order the elements of the array alphabetically by title
within author. To do that, we've implemented a custom
AuthorTitle class
that implements the
IComparer interface; its Compare method handles
the comparison of author and title strings. The code is:















































































The output of the progam shows the author and title information correctly
sorted:

Chernyshevskii, Nikolai: What Is to Be Done?
Doyle, Arthur Conan: Adventures of Sherlock Holmes, The
Doyle, Arthur Conan: Hound of the Baskervilles, The
Gogol, Nikolai: Dead Souls
London, Jack: Call of the Wild, The
London, Jack: White Fang
using System;
using System.Collections;

public class OrderStrings
{
public static void Main()
{
   string[] Books = new string[8] {"Hound of the Baskervilles, The",
                                   "White Fang",
                                   "What Is To Be Done?",
                                   "Dead Souls",
                                   "What Is to Be Done?",
                                   "Call of the Wild, The",
                                   "Adventures of Sherlock Holmes, The",
                                   
"WHAT IS TO BE DONE?"};

   Array.Sort(Books,
CaseInsensitiveComparer.Default);

   foreach (string book in Books)
      Console.WriteLine("   " + book);
}
}
using System;
using System.Collections;

public class Book
{
private string auth, bookTitle;

public Book(string a, string t)
{
   auth = a;
   bookTitle = t;
}

// public properties of Book class
public string Author
{
   get
   {
      return auth;
   }
   set
   {
      auth = value;
   }
}

public string Title
{
   get
   {
       return bookTitle;
   }
   set
   {
       bookTitle = value;
   }
}
}

public class AuthorTitle : IComparer
{

public int Compare(object x, object y)
{

   if ((x is Book) && (y is Book))
   {
      Book bkX = (Book) x;
      Book bkY = (Book) y;

      string authX = bkX.Author;
      string authY = bkY.Author;
      int result = authX.CompareTo(authY);
      if (result != 0)
         return result;
      else
      {
         string titleX = bkX.Title;
         string titleY = bkY.Title;

         return titleX.CompareTo(titleY);
      }
   }

   throw new ArgumentException("Arguments are not of type Book.");
}   
}

public class TestBooks
{
static void Main()
{
   Book[] Books = new Book[6]
       {new Book("Doyle, Arthur Conan", "Hound of the Baskervilles, The"),
        new Book("London, Jack", "White Fang"),
        new Book("Gogol, Nikolai", "Dead Souls"),
        new Book("Chernyshevskii, Nikolai", "What Is to Be Done?"),
        new Book("London, Jack", "Call of the Wild, The"),
        new Book("Doyle, Arthur Conan", "Adventures of Sherlock Holmes, The")};
   AuthorTitle autitle = new AuthorTitle();

   Array.Sort(Books, autitle);
   foreach (Book bk in Books)
      Console.WriteLine(bk.Author + ": " + bk.Title);
}
}
Howling Wolf Consulting Services

using System;
using System.Collections;

public class OrderStrings
{
public static void Main()
{
   string[] Books = new string[7] {"Hound of the Baskervilles, The",
                                   "White Fang",
                                   "What Is To Be Done?",
                                   "Dead Souls",
                                   "What Is to Be Done?",
                                   "Call of the Wild, The",
                                   "Adventures of Sherlock Holmes, The"};
   Array.Sort(Books);

   foreach (string book in Books)
      Console.WriteLine(book);
}
}
using System;
using System.Collections;

public class OrderStrings
{
public static void Main()
{
   string[] Books = new string[7] {"Hound of the Baskervilles, The",
                                   "White Fang",
                                   "What Is To Be Done?",
                                   "Dead Souls",
                                   "What Is to Be Done?",
                                   "Call of the Wild, The",
                                   "Adventures of Sherlock Holmes, The"};

   Array.Sort(Books,
Comparer.Default);

   foreach (string book in Books)
      Console.WriteLine("   " + book);
 }
}