Discussion:
Key compare in Dictionary
David Nicholson
2010-05-20 10:32:46 UTC
Permalink
I am using a class as a key to a Dictionary, and the key comparisons are not working as I expect. The example below illustrates:

// This is the key of the dictionary
namespace DictionaryTest
{
using System;
public class KeyData : IEquatable<KeyData>
{
public string F1 { get; set; }
public int F2 { get; set; }

public KeyData(string f1, int f2)
{
F1 = f1;
F2 = f2;
}

bool IEquatable<KeyData>.Equals(KeyData other)
{
if (String.Compare(F1, other.F1, true) != 0)
return false;

if (F2 != other.F2)
return false;

return true;
}
}
}

and I am using it like this:

namespace DictionaryTest
{
using System;
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
var d1 = new Dictionary<KeyData, int>();

d1.Add(new KeyData("a", 1), 1);
d1.Add(new KeyData("b", 1), 1);

KeyData kd = new KeyData("a", 1);

string mesg;
if (d1.ContainsKey(kd))
mesg = "Found";
else
mesg = "Not found";

Console.WriteLine(mesg);
}
}
}

This always returns "Not found", which isn't what I expect. A breakpoint on KeyData.Equals is never reached. The documentation for dictionary includes:

"If type TKey implements the System .IEquatable <T> generic interface, the default equality comparer uses that implementation"

I wrote an IEqualityComparer<KeyData> and passed that to the dictionary constructor, and it works as I expect.

Thanks
David.

===================================
View archives and manage your subscription(s) at http://peach.ease.lsoft.com/archives
Ryan Heath
2010-05-20 10:48:03 UTC
Permalink
I always override Object.GetHashCode and Object.Equals for these sorts
of things ...

// Ryan
Post by David Nicholson
// This is the key of the dictionary
namespace DictionaryTest
{
   using System;
   public class KeyData : IEquatable<KeyData>
   {
       public string F1 { get; set; }
       public int F2 { get; set; }
       public KeyData(string f1, int f2)
       {
           F1 = f1;
           F2 = f2;
       }
       bool IEquatable<KeyData>.Equals(KeyData other)
       {
           if (String.Compare(F1, other.F1, true) != 0)
               return false;
           if (F2 != other.F2)
               return false;
           return true;
       }
   }
}
namespace DictionaryTest
{
  using System;
  using System.Collections.Generic;
   class Program
   {
       static void Main(string[] args)
       {
           var d1 = new Dictionary<KeyData, int>();
           d1.Add(new KeyData("a", 1), 1);
           d1.Add(new KeyData("b", 1), 1);
           KeyData kd = new KeyData("a", 1);
           string mesg;
           if (d1.ContainsKey(kd))
               mesg = "Found";
           else
               mesg = "Not found";
           Console.WriteLine(mesg);
       }
   }
}
"If type TKey implements the System .IEquatable <T> generic interface, the default equality comparer uses that implementation"
I wrote an IEqualityComparer<KeyData> and passed that to the dictionary constructor, and it works as I expect.
Thanks
David.
===================================
View archives and manage your subscription(s) at http://peach.ease.lsoft.com/archives
===================================
View archives and manage your subscription(s) at http://peach.ease.lsoft.com/archives
Mike Andrews
2010-05-20 10:49:44 UTC
Permalink
You need to override GetHashCode and Equals. It needs to look something
like this:

public class KeyData : IEquatable<KeyData> {
public string F1 { get; set; }
public int F2 { get; set; }
public KeyData(string f1, int f2) {
F1 = f1;
F2 = f2;
}

public bool Equals(KeyData other) {
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Equals(other.F1, F1) && other.F2 == F2;
}
public override bool Equals(object obj) {
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof(KeyData)) return false;
return Equals((KeyData)obj);
}
public override int GetHashCode() {
unchecked {
return ((F1 != null ? F1.GetHashCode() : 0) * 397) ^ F2;
}
}
public static bool operator ==(KeyData left, KeyData right) {
return Equals(left, right);
}
public static bool operator !=(KeyData left, KeyData right) {
return !Equals(left, right);
}
}
Post by David Nicholson
I am using a class as a key to a Dictionary, and the key comparisons are
// This is the key of the dictionary
namespace DictionaryTest
{
using System;
public class KeyData : IEquatable<KeyData>
{
public string F1 { get; set; }
public int F2 { get; set; }
public KeyData(string f1, int f2)
{
F1 = f1;
F2 = f2;
}
bool IEquatable<KeyData>.Equals(KeyData other)
{
if (String.Compare(F1, other.F1, true) != 0)
return false;
if (F2 != other.F2)
return false;
return true;
}
}
}
namespace DictionaryTest
{
using System;
using System.Collections.Generic;
class Program
{
static void Main(string[] args)
{
var d1 = new Dictionary<KeyData, int>();
d1.Add(new KeyData("a", 1), 1);
d1.Add(new KeyData("b", 1), 1);
KeyData kd = new KeyData("a", 1);
string mesg;
if (d1.ContainsKey(kd))
mesg = "Found";
else
mesg = "Not found";
Console.WriteLine(mesg);
}
}
}
This always returns "Not found", which isn't what I expect. A breakpoint on
"If type TKey implements the System .IEquatable <T> generic interface, the
default equality comparer uses that implementation"
I wrote an IEqualityComparer<KeyData> and passed that to the dictionary
constructor, and it works as I expect.
Thanks
David.
===================================
View archives and manage your subscription(s) at
http://peach.ease.lsoft.com/archives
===================================
View archives and manage your subscription(s) at http://peach.ease.lsoft.com/archives
David Nicholson
2010-05-20 11:21:40 UTC
Permalink
That was it.

Thanks
David.

===================================
View archives and manage your subscription(s) at http://peach.ease.lsoft.com/archives
Mark Hurd
2010-05-21 06:31:27 UTC
Permalink
Post by David Nicholson
That was it.
Thanks
David.
Does that suggest a DocErr where it says Implementing IEquatable(Of T)
is enough for it to be used?
--
Regards,
Mark Hurd, B.Sc.(Ma.)(Hons.)

===================================
View archives and manage your subscription(s) at http://peach.ease.lsoft.com/archives
Ryan Heath
2010-05-21 07:26:54 UTC
Permalink
I have found this documentation
http://msdn.microsoft.com/en-us/library/xfhwa508.aspx

It vaguely words that a hash is required for the key class.

Perhaps it should be stated more firmly that equal keys *must* have
equal hashes, thus Object.GetHashCode *must* be overridden when
implementing IEquatable or when overriding Object.Equals.

At least the C# compiler warns with "'xxx' overrides
Object.Equals(object o) but does not override Object.GetHashCode()"
...

// Ryan
Post by Mark Hurd
Post by David Nicholson
That was it.
Thanks
David.
Does that suggest a DocErr where it says Implementing IEquatable(Of T)
is enough for it to be used?
--
Regards,
Mark Hurd, B.Sc.(Ma.)(Hons.)
===================================
View archives and manage your subscription(s) at http://peach.ease.lsoft.com/archives
===================================
View archives and manage your subscription(s) at http://peach.ease.lsoft.com/archives
Loading...