用于 OrdinalIgnoreCase 相关字符串类的 GetHashCode()

问题描述 投票:0回答:3
public class Address{
    public string ContactName {get; private set;}
    public string Company {get; private set;}
    //...
    public string Zip {get; private set;}
}

我想实现不同地址的概念,因此我重写了 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() (暂时忽略连接效率低下):

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

但这并不存在。我应该用什么来代替?或者我应该在 Equals() 方法中使用 InvariantCulture?

(我在想

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

c# .net equality
3个回答
36
投票

无论您在

Equals()
中使用什么字符串比较方法,在
GetHashCode()
中使用相同的方法都是有意义的。

无需创建临时字符串来计算哈希码。对于

StringComparison.OrdinalIgnoreCase
,请使用
StringComparer.OrdinalIgnoreCase.GetHashCode()

然后你需要将多个哈希码合并为一个。 XOR 应该没问题(因为一个人的邮政编码不太可能是另一个人的联系人姓名)。然而纯粹主义者可能不同意。

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

话虽如此,我怀疑使用像 Address 这样的复合结构作为字典的键是否明智。但该原则适用于身份类型字符串。


17
投票

两个不相等的对象可以具有相同的哈希码。尽管两个相等的对象永远不应该具有不同的哈希码。如果您使用 InvariantCulture 作为哈希码,只要 Equals 合约是按照 OrdinalIgnoreCase 实现的,它仍然是正确的。

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

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

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


8
投票

现在您可以使用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();
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.