【问题标题】:GetHashCode() for OrdinalIgnoreCase-dependent string classes与 OrdinalIgnoreCase 相关的字符串类的 GetHashCode()
【发布时间】:2012-07-13 14:44:27
【问题描述】:
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()(暂时忽略 concatenation inefficiency):

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

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

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

【问题讨论】:

  • 为什么不只是 pverride Equals(..),因为你已经这样做了?为什么要把GetHashCode(..) 和这个混在一起?
  • @Tigran 你不需要两个 Equals() 才能正常工作吗? (例如在 LINQ 中)
  • 您可以致电特别 Equals(..),然后您就完成了。这是个问题吗?
  • @Tigran 我有这些东西的IEnumerable,想致电Distinct()
  • 看起来是个棘手的问题。这让你的整个想法(无论如何都是一个小/微优化)看起来令人怀疑。

标签: c# .net equality


【解决方案1】:

无论您在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 这样的复合结构作为字典的键是否明智。但该原则适用于身份类型字符串。

【讨论】:

    【解决方案2】:

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

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

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

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

    【讨论】:

    • 我担心 InvariantCulture 会以两种不同的方式解释相同的(通常来说)字符,从而使两个相同的对象具有不同的哈希码。你是说这不可能?
    • 我用文档链接更新了我的答案。我觉得你应该很好。
    • 谢谢。无论如何,我决定保持一致,并坚持使用InvariantCultureIgnoreCase
    • 更不用说......这个实现比你能做的任何事情都要好得多。在我工作的应用程序中有许多场景,在相对较大的集合比较中,这比 ToUpperInvarient().GetHashCode() 实现好 50 倍以上。没有字符串重新分配,您不必处理土耳其语等语言中的 ToUpperInvariant 与 ToLowerInvariant 的差异。
    【解决方案3】:

    现在你可以使用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();
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2012-04-14
      • 1970-01-01
      • 1970-01-01
      • 2011-03-20
      • 2017-11-10
      • 2010-12-29
      • 2017-01-18
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多