11

I have a class that has lots of properties that I am implementing IEquitable<T> on. I have found multiple examples on how to do GetHashCode() for small amount of properties.

Here is one example

public override int GetHashCode()
{
    unchecked // Overflow is fine, just wrap
    {
        int hash = 17;
        // Suitable nullity checks etc, of course :)
        hash = hash * 23 + field1.GetHashCode();
        hash = hash * 23 + field2.GetHashCode();
        hash = hash * 23 + field3.GetHashCode();
        return hash;
    }
}

How should I go around when I have hundreds of properties on object?

Community
  • 1
  • 1
Matas Vaitkevicius
  • 58,075
  • 31
  • 238
  • 265
  • 7
    Maybe you should rethink your object design? 100s of properties in one class is bad design IMO. – Max May 05 '14 at 09:04
  • 1
    @Max Absolutely and being contractor offer to my client months of rework without some visible additional functionality. – Matas Vaitkevicius May 05 '14 at 09:06
  • 1
    Well in that case I would propably use T4 to do that... – Max May 05 '14 at 09:08
  • How about loop through the properties? – Jite May 05 '14 at 09:09
  • @Jite Looping through properties via reflection is not performance friendly ; `GetHashCode` execution time should be as short as possible. – Max May 05 '14 at 09:10
  • Is performance important? If not you could use `Reflection` to iterate trough all properties. – Christoph Fink May 05 '14 at 09:10
  • Are all properties significant for the "identity" of the object? – Hans Kesting May 05 '14 at 09:11
  • @Max I was thinking of reflection, cause I will have to do Equals() too. Still it does not feel elegant solution, on the other hand there might not be one when there are 100 of properties. Other way would be to split object adding bunch of interfaces and the do equitable on them and then call all equals on all those objects, in Equals() of this object. – Matas Vaitkevicius May 05 '14 at 09:11
  • Related: [See if this helps](http://stackoverflow.com/a/23340717/2530848) – Sriram Sakthivel May 05 '14 at 09:12
  • @Max well, is the execution time a lot longer than it would be if you wrote out all the properties like in the function above (especially when its 100's of properties)? This would also make it safe if any properties are added or removed... (I'm kinda interested in the answer, why i ask). – Jite May 05 '14 at 09:13
  • @Max I have had to design dynamic "Equality functions" in the past, often what you will do is do the reflection the first time to build a `Expression>` then just compile it and store it in a `Dictionary>` and call the pre-built compiled version every time afterward. – Scott Chamberlain May 05 '14 at 09:14
  • 1
    @Jite That is the point of T4. You can set it up to run as soon as the file is saved so any properties added or removed are taken into account in the `GetHashCode` method. And yes the execution time would be a lot longer when using reflection, just try it out. – Max May 05 '14 at 09:14
  • 1
    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) – Michael Freidgeim Jan 09 '23 at 02:49

5 Answers5

24

Spend the money to get a tool like Resharper, then just do Alt+Ins then E. This will bring up the "Generate Equality Members" dialog

enter image description here

From there just check the 100 boxes you need and it will autogenerate the GetHashCode() and Equals() functions for you

enter image description here
(the above took about 10 seconds to create)

Resharper does so much more too that it makes it worth the $150 for a personal license (you can use a personal license for work related activities without violating it, I checked). And if you are not making enough money as a programmer to afford a one time investment of $150 you really should start looking elsewhere to work as you are being very underpaid. (If you don't make any money as a programmer as you are working on a open source project Resharper is free for development teams of open source projects)

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
  • I am contractor and cannot influence the tools that my current employer uses, however I could buy personal license just for this, I think it would save enough of time to justify it's cost. – Matas Vaitkevicius May 05 '14 at 09:20
  • That is what I did. My employer would not pay for it either, so I bought a personal license for myself and verified that you can use a personal license for "For Hire" work and you can. The [***only*** limitation](http://www.jetbrains.com/resharper/buy/personal_license.html) is if you buy a personal license you ***can not*** be reimbursed for it by your company at a later date. – Scott Chamberlain May 05 '14 at 09:23
  • So I cannot buy one trough my personal company and do the work for other company that is hiring my personal one, but I can buy a personal license and use it wherever I want? – Matas Vaitkevicius May 05 '14 at 09:29
  • If you buy it for your self it will cost $150 and you can use it anywhere. If the consulting company you own buys it for you it will cost $249 (even if you are the only employee) but any single employee of your consulting company can use the software at a given time (if you do have more than one employee). Go read over their Licenses, they are well written and not hard to follow, it makes it very clear. – Scott Chamberlain May 05 '14 at 09:38
  • Thanks, this seems as my best option at the moment. – Matas Vaitkevicius May 05 '14 at 09:40
  • 1
    Or save the money and use [this answer.](https://stackoverflow.com/a/74886665/1112048) – Steffen Mangold May 16 '23 at 11:32
13

Calculate hashcode on all property values:

public override int GetHashCode()
{
    int hashCode = this.GetHashCodeOnProperties();
    return hashCode;
}

Define this extension method (which is reusable):

public static class HashCodeByPropertyExtensions
{
    public static int GetHashCodeOnProperties<T>(this T inspect)
    {
        return inspect.GetType().GetProperties().Select(o => o.GetValue(inspect)).GetListHashCode();
    }

    public static int GetListHashCode<T>(this IEnumerable<T> sequence)
    {
        return sequence
            .Where(item => item != null)
            .Select(item => item.GetHashCode())
            .Aggregate((total, nextCode) => total ^ nextCode);
    }
}
ataraxia
  • 995
  • 13
  • 31
Contango
  • 76,540
  • 58
  • 260
  • 305
  • 1
    Note: The hash code will change if any public property changes value. – Contango Feb 21 '17 at 13:57
  • Note: if two class instances have the same number of properties, and the same values, the hash code will indicate a match (it will be the same). – Contango Feb 21 '17 at 15:00
  • Note: Like all hash codes, it is not guaranteed to be unique. A good use case is deciding whether or not an object has been updated, to save time updating the object in a database or a RESTful call. – Contango Feb 21 '17 at 15:00
  • 1
    This doesn't account for null property values when calling `item.GetHashCode()` within `GetListHashCode`. It should say `return sequence.Where(item => item != null).Select(item => item.GetHashCode).Aggre.......` – ataraxia May 20 '19 at 13:03
  • @ataraxia I'm assuming that it will crash without this change, so this is probably a good change. – Contango May 20 '19 at 18:29
  • 1
    If one of the properties is null then it will crash as you would be calling `item.GetHashCode()` on an `item` that didn't exist. Apart from that it works like a charm. – ataraxia May 22 '19 at 08:13
  • I'd want to test the performance here. `GetHashCode` should be blazingly fast; if it's slower than the normal `Equals` check, that's an issue. Reflection isn't known for being very quick, see: https://stackoverflow.com/questions/25458/how-costly-is-net-reflection You'd almost want code-gen here, I'd think. – Lovethenakedgun Jun 16 '22 at 14:49
12

Old questions sometimes have new better answers HashCode.Combine:

public override int GetHashCode()
{
    return HashCode.Combine(field1, field2, field3);
}
Michael Freidgeim
  • 26,542
  • 16
  • 152
  • 170
JDC
  • 1,569
  • 16
  • 33
2

Might use this as well.. Just the overhead being a new instance of everytime you call GetHash().

new { A = Prop1, B = Prop2, C = Prop3, D = Prop4 }.GetHashCode();
bit
  • 4,407
  • 1
  • 28
  • 50
  • How is this different from: "int hash = 17; hash = hash * 23 + field1.GetHashCode(); hash = hash * 23 + field2.GetHashCode(); hash = hash * 23 + field3.GetHashCode(); return hash;" I would still have to do it field by field and there is not one class with 100 properties there are quite a few big objects. – Matas Vaitkevicius May 05 '14 at 09:22
1

If all of those properties contribute to the equality of the object (if you are not overriding equality why are you overriding GetHashCode?), then they need to include all those properties in GetHashCode.

Remember equal objects must have equal hash codes.

Better perhaps to address the question raised in the comment on the question by Max and avoid the situation. Part of this might be to consider if such types should have value semantics (equality defined by their value: is an aggregate of the value of their properties), and switch to reference semantics (each instance is unique).

Community
  • 1
  • 1
Richard
  • 106,783
  • 21
  • 203
  • 265
  • Sorry I cannot rewrite whole system, I am contractor and there is more than one massive object, I am looking for a strategy how to deal with this sort of problem. – Matas Vaitkevicius May 05 '14 at 09:17