0

I have dictionary based on a custom class. I am facing issues with overriding the GetHashCode() while inheriting IEquatable interface. My class object has three properties:

  1. Id1 (string)
  2. Id2 (string)
  3. Id3 (string)

I am retrieving data from two different sources and creating dictionaries out of these collections based on the above custom class. There are cases when one data source has two of the above ids populated whereas the other one only has one id populated. For example,

Collection1 = {Object{Id1 = null, Id2 = "NewId2", Id3 = "NewId3"}}

Collection2 = {Object{Id1 = null, Id2 = null, Id3 = "NewId3"}}

There are also cases where Id3 is null in collection2 but Id2 is not null. I am not sure how I would go about overriding GetHashCode() for such a case. I have already tried the if-elseif-else block, but there are edge cases where this logic fails:

if (Id3 != null)
{
   return Id3.GetHashCode();
}
else if (Id2 != null) {
   return Id2.GetHashCode();
}
else {
   return Id1.GetHashCode();
}

No particular order seems to work in my case. For some more context, I am trying to use the TryGetValue method from a dictionary in .NET. I looked at the source code and saw that they are using GetHashCode() there to search by keys, which is how I traced by bug to this function. Does anyone know of a way to get around this issue?

Edit: adding some examples to clarify the correct behavior

Example 1

Object1 = Object1{Id1 = null, Id2 = "K2", Id3 = "K3"};
Object2 = Object2{Id1 = null, Id2 = null, Id3 = "K3"};

Answer: object1 == object2 because Id3 matches
Example 2

Object1 = Object1{Id1 = null, Id2 = "K2", Id3 = "K3"};
Object2 = Object2{Id1 = null, Id2 = "K2", Id3 = null};

Answer: object1 == object2 because Id2 matches
Example 3

Object1 = Object1{Id1 = "K1", Id2 = null, Id3 = "K3"};
Object2 = Object2{Id1 = "K1", Id2 = "K2", Id3 = null};

Answer: object1 == object2 because Id1 matches

Edit2: a match occurs when at least of the ids between two objects is equal. For the sake of discussion, assume the following hash code mapping,

Sahil Gupta
  • 103
  • 9
  • 2
    Did you try: https://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-overriding-gethashcode?rq=1 ? – rene Jan 11 '21 at 18:59
  • I am in fact using this same algorithm. The order in which the three Ids are combined matters. In my case because the ids can randomly be missing between the two collections, I don't end up with the same hash code even if one the ids between the two object is the same. Even a linear combination of these ids to compute a hash code won't work for the example from my question – Sahil Gupta Jan 11 '21 at 19:02
  • For example, (null + "Id2" + "Id3").GetHashCode() != (null + "Id2" + null).GetHashCode(). Similarly for the algorithm from the link you posted. I am looking for a partial match, i.e. when at least of the ids match between two objects. I am implementing this logic in the overridden version of Equals(), but hash code is mismatching in such cases, which isn't working with Dictionary – Sahil Gupta Jan 11 '21 at 19:05
  • I would use : return Id1.GetHashCode() + "^" + Id2.GetHashCode() + "^" + Id3.GetHashCode(); Putting a character like "^" will work when any of the three items are null. – jdweng Jan 11 '21 at 19:06
  • How do you match the hash code for the example in the question? – Sahil Gupta Jan 11 '21 at 19:07
  • 1
    Your question sure looks like a duplicate, but per your comments, it may not be - could you please update your post with a few clear examples of what combination of fields should generate the same hash code? Also, the data type is these fields is important - looks like `string`? – CoolBots Jan 11 '21 at 19:10
  • I have added some examples in the question. How do you generate hash codes for these cases that would be equal? If it's not possible, is overriding TryGetValue the only way? – Sahil Gupta Jan 11 '21 at 19:14
  • Based on your clarification, I'd definitely go with 3 sets (or 3 dictionaries, depending on requirements) - add each item to as many dictionaries as they have non-null IDs - then union the 3 dictionaries. I wouldn't make a custom dictionary for this purpose. – CoolBots Jan 11 '21 at 19:20
  • Matching at least of one the ids is enough. I am not sure what you mean by "you basically have 3 sets, which can be unioned to get a single distinct set" – Sahil Gupta Jan 11 '21 at 19:20
  • Does this answer your question? [What is the best algorithm for overriding GetHashCode?](https://stackoverflow.com/questions/263400/what-is-the-best-algorithm-for-overriding-gethashcode) – Maytham Fahmi Jan 11 '21 at 19:21
  • Can all of these combinations of missing IDs be placed into the dictionary or are some only used for lookup? – Ben Voigt Jan 11 '21 at 19:23
  • This is what you need: `int GetHashCode() { return 0; }` It will kill performance, but all objects having the same hash code is the only thing compatible with your definition of equality. – Ben Voigt Jan 11 '21 at 19:31
  • @BenVoigt It's used only for lookup. The basic problem is comparing two collections of data where the objects are matched on at least on the Ids being equal. The pain is generating the Hash Code because the Ids are randomly missing between object in the two collections. I did consider going with return 0, but that would kill the performance, and using dictionary would become useless. I have decided to take the less elegant route of creating 6 dictionaries. I would love to discuss any other ideas you might have – Sahil Gupta Jan 11 '21 at 19:46
  • You might look at an in-memory database engine, then you can do lookups with SQL-style query expressions, and just tell it what properties to index and let the engine worry about index encoding and keeping the indexes up to date. – Ben Voigt Jan 11 '21 at 20:09

0 Answers0