1

I've stumbled into a weird problem, which does not really make sense to me.

I've got a business object Address with a property Location (with the type SqlGeography). For the sake of my requirement i have to do a lookup on the location, because there may be multiple addresses per exact location.

Since SqlGeography is a complex type i suspected that maybe the lookup isn't working because it isn't based on location coordinates for some reason so i did this:

public class Address
{
    public Address(byte[] location)
    {
        Location = SqlGeography.Deserialize(new SqlBytes(location));
    }

    public SqlGeography Location { get; set; }
}

public class SqlGeographyComparer : IEqualityComparer<SqlGeography>
{
    public bool Equals(SqlGeography x, SqlGeography y)
    {
        // !!! always entered but for some reason x + y always null
        if (x == null && y == null)
            return true;
        if (x == null ^ y == null)
            return false;

        return x.STEquals(y).IsTrue;
    }

    public int GetHashCode(SqlGeography obj)
    {
        return obj.GetHashCode();
    }
}

class Program
{
    static void Main(string[] args)
    {
        var addresses = GetAddresses();

        // should be 2 but it's 3 results
        var addressesLookup = addresses.ToLookup(d => d.Location);
        // should be 2 but it's 3 results
        var addressesLookup2 = addresses.ToLookup(d => d.Location, new SqlGeographyComparer());

        Console.ReadLine();
    }

    private static IList<Address> GetAddresses()
    {
        //              230,16,0,0,1,12,213,97,212,23,126,78,72,64,109,51,198,37,82,163,32,64
        var result = new List<Address>();

        result.Add(new Address(new byte[] { 230, 16, 0, 0, 1, 12, 213, 97, 212, 23, 126, 78, 72, 64, 109, 51, 198, 37, 82, 163, 32, 64 }));
        result.Add(new Address(new byte[] { 230, 16, 0, 0, 1, 12, 213, 97, 212, 23, 126, 78, 72, 64, 109, 51, 198, 37, 82, 163, 32, 64 }));

        result.Add(new Address(new byte[] { 230, 16, 0, 0, 1, 12, 213, 97, 212, 23, 126, 78, 72, 64, 109, 51, 198, 37, 82, 163, 32, 63 }));

        return result;
    }
}

Is this some weird bug i haven't heared about where ToLookup just doesn't pass objects into the given comparer instance?!

Dbl
  • 5,634
  • 3
  • 41
  • 66
  • 2
    Sounds like your `Location` properties are never actually populated. Although your hash code implementation doesn't look right, either... Can you provide a short but complete program demonstrating the problem? – Jon Skeet Mar 10 '15 at 14:49
  • Yes. will take a couple of minutes – Dbl Mar 10 '15 at 14:51
  • Yes, you'll need a GetHashCode(). Just to make a test (**DON'T DO IT WHEN YOU RELEASE**) do a `return 0;`... Ok... Technically if you REALLY have few elements you can do it in release :) But don't tell anyone I told you! :) – xanatos Mar 10 '15 at 14:54
  • @JonSkeet updated question with full sample – Dbl Mar 10 '15 at 15:16
  • Thanks. And have you tried the hashcode suggestion from xanatos? – Jon Skeet Mar 10 '15 at 15:17
  • 2
    Ya, GetHashCode won't work as you have it; it needs to use the values which are compared in Equals. Something as simple as XORing together the GetHashCode of each item you're comparing in in STEquals. – Andy Mar 10 '15 at 15:19
  • Yes. The reason was indeed the faulty GetHashCode... Preparing an answer to at least wrap it up – Dbl Mar 10 '15 at 15:28
  • Thanks for pointing me in the right direction. I hope some day i will forget about even asking this question... – Dbl Mar 10 '15 at 15:50

1 Answers1

0

Once i changed SqlGeographyComparer to override its method like this:

    public int GetHashCode(SqlGeography obj)
    {
        unchecked
        {
            return ((obj.Lat.GetHashCode() * 397) ^ obj.Long.GetHashCode());
        }
    }

it worked correctly.

Turns out GetHashCode has to mimic the required equality comparison (as written in Equals) in order for arguments to work properly.

Reference for implementing GetHashCode in case someone else stumbles upon this question and is wondering about 397:

What is the best algorithm for an overridden System.Object.GetHashCode?

Community
  • 1
  • 1
Dbl
  • 5,634
  • 3
  • 41
  • 66