1

I'm looking to cache objects whose uniqueness is determined by a combination of all properties within that object. The object I have is something like this:

    public double A { get; set; }
    public double B { get; set; }
    public short C { get; set; }
    public bool D { get; set; }
    public double E { get; set; }
    public double F { get; set; }
    public double G { get; set; }
    public double H { get; set; }
    public double J { get; set; }
    public double K { get; set; }
    public double[] L { get; set; }
    public double[] M { get; set; }

I could overwrite GetHashCode and do something like return A ^ B ^ C etc... However, I'm concerned that I will have many collisions.

What would be the best way to cache an object such as this?

TJF
  • 2,248
  • 4
  • 28
  • 43
  • The best way... is to overwrite GetHashCode and do something like A^B^C... If you test and there are collisions then figure out how to tweak the formula for your needs. Don't reinvent the wheel – Sten Petrov Mar 14 '13 at 22:22
  • Since your properties have public setters, there is no guarantee that the data won't change between successive invocations of `.GetHashCode`. If that state changes, it will result in completely broken behavior for anything that relies on hash codes, such as dictionaries. – Kirk Woll Mar 14 '13 at 22:33
  • You are right to be concerned; if your properties tend to "cluster" then it is possible that xoring ends up clearing a lot of bits. – Eric Lippert Mar 15 '13 at 23:14

2 Answers2

4

You can use this GetHashCode:

public override int GetHashCode()
{
    int hash = 23;
    unchecked
    {
        hash *= 17 + A.GetHashCode();
        hash *= 17 + B.GetHashCode();
        hash *= 17 + C.GetHashCode();
        // the same applies with the rest of your properties ...
        // collections must be treated differently:
        if(L != null)
        {
            hash *= 17 + L.Length;
            foreach(var d in L)
                hash *= 17 + d.GetHashCode();
        }
        if (M != null)
        {
            hash *= 17 + M.Length;
            foreach (var d in M)
                hash *= 17 + d.GetHashCode();
        }         
    }
    return hash;
}

This generates different hashcodes when different properties have the same value. If i would omit the prime-multipliers it wouldn't make a difference if A==A or A==B. The prime numbers are used to reduce the possibility of false collisions.

It also takes the arrays and their values+order into account.

This is a "must-read" on this topic: E. Lippert, Guidelines and rules for GetHashCode

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • what's the purpose of 17 and 23? – TJF Mar 14 '13 at 22:25
  • @ThomasJ.Frey: Have a look at this answer which aims this question: http://stackoverflow.com/a/3613382/284240 In short: primes help you to avoid getting the same hash-value for different input parameters. – Tim Schmelter Mar 14 '13 at 22:27
  • 1
    They also generate different hashcodes when different properties have the same value. If i would omit the 23 multiplier it wouldn't make a difference if `A`==`A` or `A`==`B`. – Tim Schmelter Mar 14 '13 at 22:33
  • 1
    @ThomasJ.Frey: This is a "must-read" on this topic: http://blogs.msdn.com/b/ericlippert/archive/2011/02/28/guidelines-and-rules-for-gethashcode.aspx – Tim Schmelter Mar 14 '13 at 22:41
  • @ThomasJ.Frey: I've edited my answer because i think that my first implementation didn't work with the `double[]`s. – Tim Schmelter Mar 15 '13 at 08:57
  • This is quite a lot of repeated code, wouldn't it make sense to make a helper method and calculate the hash code using that? Something like `return HashCode.For(A).For(B).For(C).For(L).For(M);` (possibly with a better name). – svick Mar 16 '13 at 13:15
0

A simple (altough probably not optimal) solution could be:

  1. Generate a string representation of your class. If you had only escalar properties you could do something like string.Format("{0}-{1}-{2}", A, B, C); since you have arrays, you better use a StringBuilder and compose the string within a loop.

  2. Invoke GetHashCode on the generated string.

Konamiman
  • 49,681
  • 17
  • 108
  • 138
  • I don't think this would be actually much simpler that calculating the hash code properly. Especially if you created a helper method for that. – svick Mar 16 '13 at 13:16