C
craig.wenger
I have a question about using the generic Dictionary class in .NET 2.0
with user-defined classes as the key.
MSDN says (http://msdn2.microsoft.com/en-us/library/xfhwa508.aspx):
Dictionary<TKey, TValue> requires an equality implementation to
determine whether keys are equal. You can specify an implementation of
the IEqualityComparer<T> generic interface by using a constructor that
accepts a comparer parameter; if you do not specify an implementation,
the default generic equality comparer EqualityComparer<T>.Default is
used. If type TKey implements the System.IEquatable<T> generic
interface, the default equality comparer uses that implementation.
However, I wrote a quick test and the part about IEquatable<T> does
not appear true to me.
Consider this code:
class Program
{
static void Main(string[] args)
{
Integer test1 = new Integer(5);
Integer test2 = new Integer(5);
Dictionary<Integer, object> test = new Dictionary<Integer,
object>();
test.Add(test1, null);
Console.WriteLine("test1.Equals(test2): " +
test1.Equals(test2).ToString());
Console.WriteLine("test.ContainsKey(test1): " +
test.ContainsKey(test1).ToString());
Console.WriteLine("test.ContainsKey(test2): " +
test.ContainsKey(test2).ToString());
Console.ReadKey();
}
}
public class Integer : IEquatable<Integer>
{
private int _integerValue;
public int IntegerValue
{
get { return _integerValue; }
set { _integerValue = value; }
}
public Integer(int integerValue)
{
_integerValue = integerValue;
}
#region IEquatable<Integer> Members
public bool Equals(Integer other)
{
if(other == null)
{
return false;
}
if(other._integerValue == _integerValue)
{
return true;
}
else
{
return false;
}
}
#endregion
}
The output is True, True, False. Even though test1.Equals(test2) is
True, a dictionary containing only test1 will say it contains the key
test1 but not test2 (presumably because their hash codes are
different?). Essentially implementing IEquatable<T> does not appear to
change anything with respect to the class' behavior as a key in
Dictionary<TKey, TValue>.
However, when I add code for a class that implements
IEqualityComparer<Integer>:
public class IntegerEqualityComparer : IEqualityComparer<Integer>
{
public IntegerEqualityComparer() { }
#region IEqualityComparer<Integer> Members
public bool Equals(Integer x, Integer y)
{
if(x == null || y == null)
{
return false;
}
if(x.IntegerValue == y.IntegerValue)
{
return true;
}
else
{
return false;
}
}
public int GetHashCode(Integer obj)
{
return base.GetHashCode();
}
#endregion
}
and change the Dictionary constructor call to use it:
Dictionary<Integer, object> test = new Dictionary<Integer, object>(new
IntegerEqualityComparer());
I get True for all three. This makes it seem like the dictionary with
a key of type T is not using IEquatable<T>, it only uses
IEqualityComparer<T> with the appropriate constructor overload.
with user-defined classes as the key.
MSDN says (http://msdn2.microsoft.com/en-us/library/xfhwa508.aspx):
Dictionary<TKey, TValue> requires an equality implementation to
determine whether keys are equal. You can specify an implementation of
the IEqualityComparer<T> generic interface by using a constructor that
accepts a comparer parameter; if you do not specify an implementation,
the default generic equality comparer EqualityComparer<T>.Default is
used. If type TKey implements the System.IEquatable<T> generic
interface, the default equality comparer uses that implementation.
However, I wrote a quick test and the part about IEquatable<T> does
not appear true to me.
Consider this code:
class Program
{
static void Main(string[] args)
{
Integer test1 = new Integer(5);
Integer test2 = new Integer(5);
Dictionary<Integer, object> test = new Dictionary<Integer,
object>();
test.Add(test1, null);
Console.WriteLine("test1.Equals(test2): " +
test1.Equals(test2).ToString());
Console.WriteLine("test.ContainsKey(test1): " +
test.ContainsKey(test1).ToString());
Console.WriteLine("test.ContainsKey(test2): " +
test.ContainsKey(test2).ToString());
Console.ReadKey();
}
}
public class Integer : IEquatable<Integer>
{
private int _integerValue;
public int IntegerValue
{
get { return _integerValue; }
set { _integerValue = value; }
}
public Integer(int integerValue)
{
_integerValue = integerValue;
}
#region IEquatable<Integer> Members
public bool Equals(Integer other)
{
if(other == null)
{
return false;
}
if(other._integerValue == _integerValue)
{
return true;
}
else
{
return false;
}
}
#endregion
}
The output is True, True, False. Even though test1.Equals(test2) is
True, a dictionary containing only test1 will say it contains the key
test1 but not test2 (presumably because their hash codes are
different?). Essentially implementing IEquatable<T> does not appear to
change anything with respect to the class' behavior as a key in
Dictionary<TKey, TValue>.
However, when I add code for a class that implements
IEqualityComparer<Integer>:
public class IntegerEqualityComparer : IEqualityComparer<Integer>
{
public IntegerEqualityComparer() { }
#region IEqualityComparer<Integer> Members
public bool Equals(Integer x, Integer y)
{
if(x == null || y == null)
{
return false;
}
if(x.IntegerValue == y.IntegerValue)
{
return true;
}
else
{
return false;
}
}
public int GetHashCode(Integer obj)
{
return base.GetHashCode();
}
#endregion
}
and change the Dictionary constructor call to use it:
Dictionary<Integer, object> test = new Dictionary<Integer, object>(new
IntegerEqualityComparer());
I get True for all three. This makes it seem like the dictionary with
a key of type T is not using IEquatable<T>, it only uses
IEqualityComparer<T> with the appropriate constructor overload.