如何使用重写后的Equals()逻辑来实现重写GetHashCode()。

7
我有以下一些类,我已经为它们中的几乎所有类实现了Equals(Object)方法。但是我不知道如何编写GetHashCode()方法。在将这些数据类型用作值类型存储在Dictionary集合中时,我认为我应该重写GetHashCode()方法。
1. 我不知道如何根据Equals(Object)方法的逻辑来实现GetHashCode()方法。
2. 如果我为基类(Param)覆盖GetHashCode()Equals(Object)方法,是否仍然需要为子类覆盖它们?
class Param
{
    ...
    public Int16 id { get; set; }
    public String name { get; set; }
    ...
    public override bool  Equals(object obj)
    {
        if ( obj is Param){
            Param p = (Param)(obj);
            if (id > 0 && p.id > 0)
                return (id == p.id);
            else if (name != String.Empty && p.name != String.Empty)
                return (name.equals(p.name));
            else
                return object.ReferenceEquals(this, obj);
        }
        return false;
    }
}  
class Item
{
    public int it_code { get; set; }
    public Dictionary<String, Param> paramAr { get; set; }
    ...
    public override bool Equals(Object obj)
    {
        Item im = new Item();
        if (obj is Item)
            im = (Item)obj;
        else 
            return false;

        if (this.it_code != String.Empty && im.it_code != String.Empty)
            if (this.it_code.Equals(im.it_code)) 
                return true;

        bool reParams = true;
        foreach ( KeyValuePair<String,Param> kvp in paramAr ){
            if (kvp.Value != im.paramAr[kvp.Key]) {
                reParams = false;
                break;
            }
        }
        return reParams;
    }
}
class Order
{

    public String or_code { get; set; }
    public List <Item> items { get; set; }
    ...
    public override bool Equals( Object obj ){
        Order o = new Order();
        if (obj is Order)
            o = (Order)obj;
        else
            return false;

        if (this.or_code != String.Empty && o.or_code != String.Empty)
            if (this.or_code.Equals(o.or_code))
                return true;
        bool flag = true;
        foreach( Item i in  items){
            if (!o.items.Contains(i)) { 
                flag = false;
                break;
            }
        }
        return flag;
    }
}

编辑: 我收到了以下警告:

警告:'Item' 重写了 Object.Equals(object o),但没有重写 Object.GetHashCode()


1
我相信在编写 GetHashCode 时,你唯一需要考虑的是当两个对象通过调用 Equals 相等时,它们的哈希码也必须相等。反之,如果两个哈希码相等,并不意味着对象本身相等(尽管它们可能相等)。每当你编写自定义实现 EqualsGetHashCode 的方法时,你都需要为另一个方法编写一些内容,以确保上述逻辑保持正确。 - Matthew
现在Visual Studio可以帮助解决这个问题了 https://learn.microsoft.com/en-us/visualstudio/ide/reference/generate-equals-gethashcode-methods?view=vs-2017 - Nick
2个回答

17
首先,就像你理解的那样,无论在何处实现Equals,你都必须同时实现GetHashCodeGetHashCode 的实现必须反映Equals 实现的行为,但通常不会使用它。
请参阅http://msdn.microsoft.com/en-us/library/system.object.gethashcode.aspx,特别是"Notes to Implementers"。
因此,如果以Item实现Equals为例,你考虑了idname的值来影响相等性。所以这两个值都必须对GetHashCode实现做出贡献。
以下是如何为Item实现GetHashCode的示例(请注意,您可能需要使其能够处理可空的name字段):
public override GetHashCode()
{
    return id.GetHashCode() ^ name.GetHashCode();
}

参见Eric Lippert有关GetHashCode的指南:http://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/

至于在子类中是否需要重新实现GetHashCode,如果您还要重写Equals,则是必须的——根据第一个(和主要)要点,两者的实现必须一致——如果两个项目被Equals视为相等,则它们必须从GetHashCode返回相同的值。

附注: 为了提高代码性能(避免多次强制转换):

if ( obj is Param){
    Param p = (Param)(obj);

Param p = obj as Param;
if (p != null) ...

关于 order 类,它只有一个 id 和一个列表,我无法使用其列表中的所有值来生成哈希码。 - rene
一个List对象有一个GetHashCode实现,这可能已经足够满足您的需求。因为列表影响相等性,所以您必须在GetHashCode实现中考虑它。然而,再次查看您的Equals实现,您正在按层次结构使用它们 - 如果ID匹配,则对象相等,而不考虑其名称值。因此,GetHashCode实现需要与我的初始建议不同;我还没有想出如何做...(但)。 - kaj
@rene 如果你在Equals方法中做的就是这个,那么是的,这正是你需要做的。 - Servy
1
@KAJ List的GetHashCode将基于列表的引用而不是其内容。具有相同项目的两个不同列表不一定具有相同的哈希码。现在,如果您定义了订单的相等性仅基于订单代码而不是项目,则这些问题将消失。Equals可以比较订单代码,getHashCode可以返回订单代码的哈希码。如果程序需要支持具有相同订单代码但不同项目的两个不同订单,则需要在equals和GetHashCode中使用列表中的每个项目。 - Servy
@Servy:这正是我所想的(即将尝试一个概念验证)。基本问题是,GetHashCode应该被快速评估,因此迭代列表中的所有项听起来不是一个好主意;但是,如果它是相等性测试的一部分,那么它必须被包括在内。 - kaj
显示剩余2条评论

9
我更喜欢Josh Bloch的方法。
以下是Param类的示例。
override GetHashCode(object obj)
{
 unchecked
    {
        int hash = 17;

        hash = hash * 23 + id.GetHashCode();
        hash = hash * 23 + name.GetHashCode();
        return hash;
    }
}

此外,请查看此链接:.net - GetHashCode的最佳算法。用于计算哈希码的属性也应该是不可变的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接