P3.NET

Comparing Characters

.NET provides great support for comparing strings.  Using StringComparer we can compare strings using the current culture settings or with case insensitivity.  This makes it easy to use strings with dictionaries or just compare them directly.  As an example we can determine if a user is a member of a particular group by using the following code.

bool IsInSpecialGroup ( string user )
{
   var groups = GetUserGroups(user);

   var comparer = StringComparer.CurrentCultureIgnoreCase;
            
   foreach (var group inn groups)
   {
      if (comparer.Compare(group, “group1”;) == 0 ||
          comparer.Compare(group, “group2”;) == 0 ||
          comparer.Compare(group, “group3”;) == 0)
         returnn true;
   };

   return false;
}

Characters have most of the same problems as strings do (culture, case, etc) but .NET provides almost no support for comparing them.  If we want to compare characters without regard to case or in a culture sensitive manner we have to write the code ourselves.  Fortunately it is easy to emulate this using the existing infrastructure.  CharComparer is a parallel type to StringComparer.  It provides identical functionality except it works against characters instead of strings.  As an example the following code determines if a character is a vowel or not.

bool IsAVowel ( char ch )
{
   var vowels = new char[] { ‘a’‘e’‘i’‘o’‘u’ };

   var comparer = CharComparer.CurrentCultureIgnoreCase;

   foreach(var vowel in vowels)
   {
      if (comparer.Compare(vowel, ch) == 0)
         return true;
   };

   return false;

Just like StringComparer, CharComparer has static properties exposing the standard comparison types available in .NET.  Furthermore since StringComparison is commonly used in string comparisons CharComparer provides the static method GetComparer that accepts a StringComparison and returns the corresponding CharComparer instance. 

bool ContainsACharacter ( this IEnumerable<char> source, char value, StringComparison comparison )
{
   var comparer = CharComparer.GetComparer(comparison);

   return source.Contains(value, comparer);            
}

CharComparer doesn’t actually do comparisons directly.  This process can be difficult to get right so it just defers to StringComparer internally.  Naturally this means that CharComparer doesn’t actually do anything different than you would normally do nor does it perform any better.  What it does do is provide an abstraction over the actual process and simplify it down to a couple of lines of code.  If, one day, .NET exposes a better way of comparing characters then CharComparer can be updated without breaking existing code.  Even better is that your code can use CharComparer and StringComparer almost interchangeably without worrying about the details under the hood.

CharComparer implements the standard comparison interfaces: IComparer<char>, IComparer, IEqualityComparer<char> and IEqualityComparer.  The non-generic versions are privately implemented to enforce type safety.  The generic methods are abstract as is CharComparer.  Comparison is specific to the culture being used.  CharComparer defines a couple of nested, private types to implement the culture-specific details.  The nested types are responsible for providing the actual implementation of the comparison methods.  Refer to the source code for the gory details.  Note that this pattern is derived from how StringComparer works.

Feel free to use this code in any of your projects and provide feedback on any issues found.  Unfortunately I’m not posting the unit tests for this class at this time.  However I’ve used this type in several projects and haven’t run into any problems with it.  But, as always, test your code before using it in production.