3

I am trying to do something along the lines of the following:

class Test
{
     public string Name { get; set;}
     public string Location { get; set;}
     public Test(string name, string location)
     {
         Name = name;
         Location = location;
     }
}

Now, in a method in another class, I am trying to add these Test classes into a Dictionary with a KeyValuePair of

Dictionary<Test,int> resources = new Dictionary<Test,int>();
resources.Add(new Test("First Resource", "Home"), 1);

Now, what I am trying to do, and need to be able to do is:

bool contains = resources.ContainsKey(new Test("First Resource", "Home"));
resources[new Test("First Resource", "Home")] = 2;

As of now, this returns false. How can I get this to return true?

I have tried overriding the Equals function of my Test class and even implementing IComparible and doing custom comparisons.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Kyle Uithoven
  • 2,414
  • 5
  • 30
  • 43

3 Answers3

4

You need to implement GetHashCode and Equals in the key class or provide an IEqualityComparer<Test> implementation in the constructor of the dictionary.

In the case of the comparer, you would define proper GetHashCode and Equals methods inside the comparer for Test, with the benefit that these implementations are not universal for all Test objects, but can be used at will when necessary (such as for use in dictionaries, hashsets, various Linq queries, etc.) By decoupling the equality and hashcode functions from the class, you are then free to use different implementations of the comparer as the need arises.

(For a good set of guidelines on GetHashCode, please visit this blog.)

Anthony Pegram
  • 123,721
  • 27
  • 225
  • 246
4

You need to override GetHashCode in your Test class, add the following to your class:

public override int GetHashCode()
{
    return (Name+Location).GetHashCode();
}

This will ensure that any two Test instances have the same hash only if the concatenation of Name and Location are the same. You could use other strategies for this, however, this is the simplest form.

Candide
  • 30,469
  • 8
  • 53
  • 60
  • "This will ensure that any two Test instances are the same only if the concatenation of Name and Location are the same. " This is not ensured by `GetHashCode()`. There are infinite number of possible combinations of `Name+Location`. There are only 2^32 possible values of `GetHashCode()`. – Anthony Pegram Oct 13 '11 at 17:14
  • @AnthonyPegram : So, what does that entail? Should I only perform the (Name+Location)? Can I just omit the GetHashCode()? – Kyle Uithoven Oct 13 '11 at 17:15
  • @AnthonyPegram Good catch, it is a dumb hash, I couldn't claim it isn't. – Candide Oct 13 '11 at 17:16
  • @Kyle The `GetHashCode()` implementation is fine. Anthony's point is that this in no way guarantees a unique hash for each name/location pair (which is fine, since that's not what a hash code does.) – dlev Oct 13 '11 at 17:17
  • @KyleUithoven, no, you can still use these properties as a basis for the value returned by `GetHashCode()`, what I was challenging was the statement that `GetHashCode()` can determine whether two instances are the *same.* That's not its job, it's a first pass of sorts towards eventually checking for equality inside a hashtable, but it is not a test of equality in and of itself. The basic point is two non-equal objects can have the same hash code, but two equal objects should not have different hash codes. – Anthony Pegram Oct 13 '11 at 17:17
  • @dlev : If each pair does NOT have a unique pair, does that not mean that I will run into problems down the road? How can I ensure a unique code? – Kyle Uithoven Oct 13 '11 at 17:18
  • @KyleUithoven Just don't allow one to put arbitrarily large strings in the name and location fields. Limit these fields to some reasonable length. – Candide Oct 13 '11 at 17:19
  • @KyleUithoven, *you don't*. Don't worry about *uniqueness* for a hash code. That's not its job. Worry about a balanced distribution and correctness. I've added a link to my answer, but I encourage you to read http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx – Anthony Pegram Oct 13 '11 at 17:20
  • @AnthonyPegram : Thank you for that explanation, I now get what you are talking about. Two unequal objects can have the same hash code but that does not mean that it will return equal. That job lies in the equal function. – Kyle Uithoven Oct 13 '11 at 17:20
  • @Kyle Think of it this way: `GetHashCode()` returns an `int`, which means there are 2^32 possible hash codes possible. There are *way more* than 2^32 possible strings of characters, so obviously there must be collisions. That's fine, though, since the goal of a hash function is to ensure that random input to a hash table will have a reasonably balanced distribution of hash values. – dlev Oct 13 '11 at 17:21
  • @dlev : And that's why the equal function exists? Will the ContainsKey only enter the equal function when hash codes are equal? So even if there are two unequal objects with the same hash code, the equal function makes SURE that they are equal. – Kyle Uithoven Oct 13 '11 at 17:23
  • @Kyle Bingo. `GetHashCode()` narrows down the list of candidates, then `Equals()` takes it home. – dlev Oct 13 '11 at 17:25
3

Dictionary uses GetHashCode of the key to determine in which data bucket to store an object, and then Equals to make sure that the objects are actually equal. In other words, for this to work, you will need to implement GetHashCode() and Equals for your type.

It is considered good practice to make objects used for keys in a dictionary immutable. If the object changes, its hashcode changes, and you might not be able to find it in the dictionary.

Community
  • 1
  • 1
driis
  • 161,458
  • 45
  • 265
  • 341
  • I have tried implementing GetHashCode() and I have no idea what to return. What would be a good way to do that? Convert the name and position into integer values and add them together? – Kyle Uithoven Oct 13 '11 at 17:11
  • @Kyle - anything that returns the same value for the "same" object is fine. it's ok but non-ideal to return the same value for "different" objects. what you suggest is fine as is ingenu's suggestion – Robert Levy Oct 13 '11 at 17:15
  • Please see this excellent answer for instructions on how to implement GetHashCode: http://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-an-overridden-system-object-gethashcode/263416#263416 – driis Oct 13 '11 at 17:16