I need to create a set of unique Font objects each of which should have an associated object. This should be done in a .NET Framework WinForms app. These Font objects come from various sources, and different Font objects can be actually the same - if they have the same name, size, and other characteristics. It seems the Hashtable class can be used for this. Here is a simple test in a brand new WinForms app for .NET Framework:
private void Form1_Load(object sender, EventArgs e)
{
Font font1 = new Font(this.Font, FontStyle.Regular);
Font font2 = new Font("Microsoft Sans Serif", 8.25f);
Hashtable ht = new Hashtable();
ht.Add(font1, 1);
ht.Add(font2, 2);
}
The last statement that adds font2 throws the following exception:
System.ArgumentException HResult=0x80070057 Message=Item has already been added. Key in dictionary: '[Font: Name=Microsoft Sans Serif, Size=8.25, Units=3, GdiCharSet=1, GdiVerticalFont=False]' Key being added: '[Font: Name=Microsoft Sans Serif, Size=8.25, Units=3, GdiCharSet=1, GdiVerticalFont=False]' Source=mscorlib StackTrace: at System.Collections.Hashtable.Insert(Object key, Object nvalue, Boolean add) at System.Collections.Hashtable.Add(Object key, Object value) at Font_Hashtable.Form1.Form1_Load(Object sender, EventArgs e)
This proves that the specialized Font equality comparer is used to check if Font objects are used as Hashtable keys. I wonder - can we really use a Hashtable for this scenario? As I know, .NET Hashtables should compare keys by references if they are objects, and there should be no exception in the code above if this is the case.
The Hashtable
checks in Add
if the key is already contained, then it will throw this exception. Therefore it checks if there is one with the same hashcode. If so it checks if they are equal, first by checking if it's the same same reference(object.ReferenceEquals
). If not it checks if they are equal. Either by the given KeyComaprer
in the constructor or by object.Equals
. If Equals
is overridden, Font
overrides Equals
, that one is used.
The documentation mentions it also:
"The default hash code provider is each key's implementation of GetHashCode() and the default comparer is each key's implementation of Equals(Object)."
Since GetHashCode
is used first, always override Equals
and GetHashCode
together. Two equal objects must have the same hashcode(but same hashcode doesn't necessarily mean equal).
By the way, there is no reason to use the old Hashtable
anymore. Here you should use a Dictionary<Font, float>
.
If you use a HashTable
or a generic Dictionary<TKey, TValue>
does not make a difference for this issue: both are checking in Add
if there is another item with the same hash-code and if so, if both items are equal, then you get this ArgumentException
.
Both use either the passed custom IEqalityComparer
(constructor) or Equals
(if not overridden object.Equals
, which just compares references).