Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GetHashCode() for OrdinalIgnoreCase-dependent string classes

Tags:

c#

.net

equality

public class Address{
    public string ContactName {get; private set;}
    public string Company {get; private set;}
    //...
    public string Zip {get; private set;}
}

I'd like to implement a notion of distint addresses, so I overrode Equals() to test for case-insensitive equality in all of the fields (as these are US addresses, I used Ordinal instead of InvariantCulture for maximum performance):

public override bool Equals(Object obj){
    if (obj == null || this.GetType() != obj.GetType())
        return false;

    Address o = (Address)obj;

    return  
    (string.Compare(this.ContactName, o.ContactName, StringComparison.OrdinalIgnoreCase) == 0) &&
    (string.Compare(this.Company, o.Company, StringComparison.OrdinalIgnoreCase) == 0)
    // ...
    (string.Compare(this.Zip, o.Zip, StringComparison.OrdinalIgnoreCase) == 0)
}

I'd like to write a GetHashCode() similarly like so (ignore the concatenation inefficiency for the moment):

public override int GetHashCode(){
    return (this.contactName + this.address1 + this.zip).ToLowerOrdinal().GetHashCode();
}

but that doesn't exist. What should I use instead? Or should I just use InvariantCulture in my Equals() method?

(I'm thinking .ToLowerInvariant().GetHashCode(), but I'm not 100% sure that InvariantCulture can't decide that an identical character (such as an accent) has a different meaning in another context.)

like image 794
Arithmomaniac Avatar asked Jul 13 '12 17:07

Arithmomaniac


1 Answers

Whatever string comparison method you use in Equals(), it makes sense to use the same in GetHashCode().

There's no need to create temporary strings just to calculate hash codes. For StringComparison.OrdinalIgnoreCase, use StringComparer.OrdinalIgnoreCase.GetHashCode()

Then you need to combine multiple hash codes into one. XOR should be ok (because it's unlikely that one person's zip code is another's contact name). However purists might disagree.

public override int GetHashCode()
{
    return StringComparer.OrdinalIgnoreCase.GetHashCode(ContactName) ^
        StringComparer.OrdinalIgnoreCase.GetHashCode(Company) ^
        // ...
        StringComparer.OrdinalIgnoreCase.GetHashCode(Zip);
}

Having said all that, I'd question whether it's sensible to use a composite structure like Address as the key to a dictionary. But the principle holds for identity-type strings.

like image 147
ben Avatar answered Oct 25 '22 16:10

ben