GetHashCode()用于OrdinalIgnoreCase依赖的字符串类

时间:2012-07-13 17:52:51

标签: c# .net equality

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

我想实现一个distint地址的概念,所以我重写了Equals()以测试所有字段中的不区分大小写的相等性(因为这些是美国地址,我使用Ordinal而不是InvariantCulture来获得最大性能) :

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)
}

我想像这样写一个GetHashCode()(暂时忽略concatenation inefficiency):

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

但这不存在。我应该用什么呢?或者我应该在我的Equals()方法中使用InvariantCulture吗?

(我在想.ToLowerInvariant().GetHashCode(),但我并不是100%确定InvariantCulture不能确定相同的字符(例如重音)在另一个上下文中具有不同的含义。)

3 个答案:

答案 0 :(得分:21)

无论您在Equals()中使用哪种字符串比较方法,都可以在GetHashCode()中使用相同的方法。

不需要创建临时字符串来计算哈希码。对于StringComparison.OrdinalIgnoreCase,请使用StringComparer.OrdinalIgnoreCase.GetHashCode()

然后,您需要将多个哈希码合并为一个。 XOR应该没问题(因为一个人的邮政编码不太可能是另一个人的联系人姓名)。但purists可能不同意。

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

说了这么多之后,我一直怀疑使用像Address这样的复合结构作为词典的关键是否合理。但这个原则适用于身份类型的字符串。

答案 1 :(得分:14)

两个不等的对象可以具有相同的哈希码。虽然两个相等的对象永远不应该有不同的哈希码。如果你使用InvariantCulture作为你的哈希码,那么如果它是用OrdinalIgnoreCase实现的话,那么Equals的合同仍然是正确的。

从StringComparer.OrdinalIgnoreCase(强调我的)文档:

http://msdn.microsoft.com/en-us/library/system.stringcomparer.ordinalignorecase.aspx

  

OrdinalIgnoreCase属性返回的StringComparer处理   要比较的字符串中的字符就好像它们被转换为一样   大写使用 不变文化 的约定,以及   然后执行一个独立于的简单字节比较   语言。这在比较字符串时最合适   以编程方式生成或在比较不区分大小写时生成   路径和文件名等资源。

答案 2 :(得分:1)

现在您可以使用System.HashCode

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

    public override bool Equals(object obj)
    {
        return
            obj is Address address &&
            string.Equals(ContactName, address.ContactName, StringComparison.OrdinalIgnoreCase) &&
            string.Equals(Company, address.Company, StringComparison.OrdinalIgnoreCase) &&
            // ...
            string.Equals(Zip, address.Zip, StringComparison.OrdinalIgnoreCase);
    }

    public override int GetHashCode()
    {
        var hash = new HashCode();
        hash.Add(ContactName, StringComparer.OrdinalIgnoreCase);
        hash.Add(Company, StringComparer.OrdinalIgnoreCase);
        // ...
        hash.Add(Zip, StringComparer.OrdinalIgnoreCase);
        return hash.ToHashCode();
    }
}