Monday 17 December 2012

c sharp programming

Plugging in Equality and Order


A type’s default equating or comparison implementation typically reflects what is
most “natural” for that type. Sometimes, however, the default behavior is not what
you want.  You might  need  a  dictionary whose  string-type  key  is  treated  case-
insensitively. Or you might want a sorted list of customers, sorted by each customer’s
postcode. For this reason, the .NET Framework also defines a matching set of “plug-
in” protocols. The plug-in protocols achieve two things:

• They allow you to switch in alternative equating or comparison behavior.
• They allow you to use a dictionary or sorted collection with a key type that’s
not intrinsically equatable or comparable.

The plug-in protocols consist of the following interfaces:
IEqualityComparer and IEqualityComparer<T>
• Performs plug-in equality comparison and hashing
• Recognized by Hashtable and Dictionary
IComparer and IComparer<T>
• Performs plug-in order comparison
• Recognized by the sorted dictionaries and collections; also, Array.Sort
Each interface comes in generic and nongeneric forms. The IEqualityComparer in-
terfaces also have a default implementation in a class called EqualityComparer.
In addition, Framework 4.0 adds two new interfaces called IStructuralEquatable
and IStructuralComparable, which allow the option of structural comparisons on
classes and arrays.


IEqualityComparer and EqualityComparer
An equality comparer switches  in nondefault equality and hashing behavior, pri-
marily for the Dictionary and Hashtable classes.

Recall  the requirements of a hashtable-based dictionary.  It needs answers  to  two
questions for any given key:
• Is it the same as another?
• What is its integer hash code?
An  equality  comparer  answers  these  questions  by  implementing  the
IEqualityComparer interfaces:
public interface IEqualityComparer<T>
{
   bool Equals (T x, T y);
   int GetHashCode (T obj);
}
public interface IEqualityComparer     // Nongeneric version
{
   bool Equals (object x, object y);
   int GetHashCode (object obj);
}
To write a custom comparer, you implement one or both of these interfaces (im-
plementing both gives maximum interoperability). As this is somewhat tedious, an
alternative is to subclass the abstract EqualityComparer class, defined as follows:

public abstract class EqualityComparer<T> : IEqualityComparer,
                                            IEqualityComparer<T>
{
  public abstract bool Equals (T x, T y);
  public abstract int GetHashCode (T obj);
  bool IEqualityComparer.Equals (object x, object y);
  int IEqualityComparer.GetHashCode (object obj);
  public static EqualityComparer<T> Default { get; }
}
EqualityComparer implements both interfaces; your job is simply to override the two
abstract methods.

The semantics for Equals and GetHashCode follow the same rules for object.Equals
and object.GetHashCode, In the following example, we define
a Customer class with two fields, and then write an equality comparer that matches
both the first and last names:
public class Customer
{
  public string LastName;
  public string FirstName;
  public Customer (string last, string first)
  {
    LastName = last;
    FirstName = first;
  }
}

public class LastFirstEqComparer : EqualityComparer <Customer>
{
  public override bool Equals (Customer x, Customer y)
  {
    return x.LastName == y.LastName && x.FirstName == y.FirstName;
  }
  public override int GetHashCode (Customer obj)
  {
    return (obj.LastName + ";" + obj.FirstName).GetHashCode();
  }
}
To illustrate how this works, we’ll create two customers:
Customer c1 = new Customer ("Bloggs", "Joe");
Customer c2 = new Customer ("Bloggs", "Joe");
Because we haven’t overridden object.Equals, normal reference  type equality se-
mantics apply:
Console.WriteLine (c1 == c2);               // False
Console.WriteLine (c1.Equals (c2));         // False
The  same  default  equality  semantics  apply  when  using  these  customers  in  a
Dictionary without specifying an equality comparer:
var d = new Dictionary<Customer, string>();
d [c1] = "Joe";
Console.WriteLine (d.ContainsKey (c2));         // False

Now with the custom equality comparer:
var eqComparer = new LastFirstEqComparer();
var d = new Dictionary<Customer, string> (eqComparer);
d [c1] = "Joe";
Console.WriteLine (d.ContainsKey (c2));         // True
In  this  example,  we  would  have  to  be  careful  not  to  change  the  customer’s
FirstName or LastName while it was in use in the dictionary. Otherwise, its hash code
would change and the Dictionary would break.


EqualityComparer<T>.Default
Calling EqualityComparer<T>.Default returns a general-purpose equality comparer
that can be used as an alternative to the static object.Equals method. The advantage
is that first checks if T implements IEquatable<T> and if so, calls that implementation
instead, avoiding the boxing overhead. This is particularly useful in generic methods:
static bool Foo<T> (T x, T y)
 {
   bool same = EqualityComparer<T>.Default.Equals (x, y);
  ...

No comments:

Post a Comment