C# 理解行为与行为;重写GetHashCode()

C# 理解行为与行为;重写GetHashCode(),c#,dictionary,C#,Dictionary,我试图遵循MSDN中的,也提到了,但下面的行为似乎不符合预期 我试图表示一个类似于FQN的结构,其中好像P1在P2之前列出,P2将只存在于与P1相同的集合中。比如范围是如何工作的 关于GetHashCode()的主题 我有一个类具有这样的属性 class data{ public readonly string p1, p2; public data(string p1, string p2) { this.p1 = p1; this.p2 = p2;

我试图遵循MSDN中的,也提到了,但下面的行为似乎不符合预期

我试图表示一个类似于FQN的结构,其中好像P1P2之前列出,P2将只存在于与P1相同的集合中。比如范围是如何工作的


关于GetHashCode()的主题 我有一个类具有这样的属性

class data{
   public readonly string p1, p2;
   public data(string p1, string p2) {
       this.p1 = p1;
       this.p2 = p2;
   }
   public override int GetHashCode()
   {
       return this.p1.GetHashCode() ^ this.p2.GetHashCode();
   }
  /*also show the equal for comparison*/
    public override bool Equals(System.Object obj)
    {
        if (obj == null)
            return false;
        data d = obj as data;
        if ((System.Object)d == null)
            return false;
        /*I thought this would be smart*/
        return d.ToString() == this.ToString();
    }
    public override string ToString() {
        return "[" + p1 +"][" + p2+ "]";
    }
}
字典
(dict)中,我使用
数据
作为键,因此这将使作用域看起来像
d1.p1.p2
(或者更确切地说是d1中p1的p2,但您更愿意想象它)

结果是两个元素都4

  • GetHashCode()是否被正确重写
  • 是否正确重写了Equals
  • 如果他们都很好,这种行为是怎么发生的

  • 关于词典的主题 在监视窗口(VS2013)中,我让字典的键列表显示给我,而不是像我通常期望的那样,每个索引都有一个键,我的数据对象的每个属性都是单个索引的键。因此,我不确定问题是否存在,或者我只是误解了Watch窗口将对象表示为键。我知道VS显示对象的方式是这样的,但我不确定字典中键的显示方式是这样的

  • 我认为GetHashCode()是字典的主要“比较”操作,这总是正确的吗
  • 键是对象的字典的真正“索引”是什么
  • 更新 在直接查看每个哈希代码之后,我注意到它们确实重复。然而,字典并没有确定索引是否存在。下面是我看到的数据示例

    1132917379      string: [ABC][ABC]   
    -565659420      string: [ABC][123]  
    -1936108909     string: [123][123]  
    //second loop with next set of strings   
    1132917379      string: [xxx][xxx]  
    -565659420      string: [xxx][yyy]
    //...etc
    
  • GetHachCode()是否被正确重写
  • 当然,对于“正确”的一些定义。它可能不会被很好地重写,但它不是一个错误的实现(因为被认为相等的两个类实例将散列到相同的值)。当然,对于这个要求,您可以始终从
    GetHashCode
    返回0,它将是“正确的”。那肯定不好

    也就是说,您的特定实现并没有它可能的那么好。例如,在您的类中,字符串的顺序很重要。即
    新数据(“A”、“B”)!=新数据(“B”、“A”)
    。但是,由于
    GetHashCode
    实现是对称的,所以这些哈希值总是相等的。最好以某种方式打破对称。例如:

    public int GetHashCode()
    {
        return p1.GetHashCode() ^ ( 13 * p2.GetHashCode() );
    }
    
    现在,两个不相等的实例发生碰撞的可能性降低了

  • 是否正确覆盖了Equal
  • 当然,这是可以改进的。例如,第一个空检查是冗余的,第二个比较中对
    对象的转换也是冗余的。整件事最好写成:

    public bool Equals( object obj )
    {
        var other = obj as data;
        if( other == null ) return false;
        return p1 == obj.p1 && p2 == obj.p2;
    }
    
    我还删除了对
    ToString
    的调用,因为它不会显著简化代码或使其更具可读性。这也是执行比较的一种低效方法,因为在进行比较之前必须构造两个新字符串。直接比较成员可以让您有更多的机会提前退出,更重要的是,更容易阅读(实际的平等实现不依赖于字符串表示)

  • 如果他们都很好,这种行为是怎么发生的
  • 我不知道,因为你给的代码不能这样做。它也不会编译。您的
    数据
    类有两个
    只读
    字段,您不能使用初始值设定项列表分配这些字段,正如您在上一个代码段中所示

    我只能推测你所看到的行为,因为你在这里展示的任何东西都不会导致所描述的行为

    我能给出的最好建议是确保您的密钥类是不可变的可变类型不能很好地使用
    字典
    Dictionary
    类不希望对象的哈希代码发生更改,因此如果
    GetHashCode
    依赖于类中任何可变的部分,则很可能会将事情弄得一团糟

  • 我认为GetHachCode()是字典的主要“比较”操作,这总是正确的吗
  • Dictionary
    仅使用
    GetHashCode
    作为“寻址”对象的一种方式(具体来说,哈希代码用于标识项目应放在哪个bucket中)。它不一定直接用作比较。如果是这样,它只能用它来确定两个物体不相等,它不能用它来确定它们是否相等

  • 键是对象的字典的真正“索引”是什么
  • 我不完全确定你在问什么,但我倾向于说答案是这无关紧要。物品的去向并不重要。如果你关心这类事情,你可能不应该使用
    字典

    GetHashCode()是否被正确重写

    否。您允许为
    p1
    p2
    null传递
    null
    。GetHashCode()
    抛出
    NullReferenceException
    中不允许的
    NullReferenceException
    。禁止传递
    null
    ,或使
    GetHashCode
    为null返回
    int
    (可以为零)

    你也在对不变的整数进行XORing;这意味着您创建的每个包含两个相同值的类的hashCode为零。这是很常见的碰撞,;通常情况下,将每个哈希代码乘以一个素数以避免出现这种情况

    是否正确重写了Equals

    否。您链接到的页面是由
    System.Collections.HashTable
    使用的非通用
    Equals
    。您正在使用generic
    System.Collections.generic.Dictionary
    ,它使用
    public int GetHashCode()
    {
        return p1.GetHashCode() ^ ( 13 * p2.GetHashCode() );
    }
    
    public bool Equals( object obj )
    {
        var other = obj as data;
        if( other == null ) return false;
        return p1 == obj.p1 && p2 == obj.p2;
    }
    
    public class MatPair : IEquatable<MatPair>
    {
        public readonly string MatNeedsToExplainWhatThisRepresents;
        public readonly string MatNeedsToExplainThisToo;
    
        public MatPair(string matNeedsToExplainWhatThisRepresents,
            string matNeedsToExplainThisToo)
        {
            if (matNeedsToExplainWhatThisRepresents == null) throw new ArgumentNullException("matNeedsToExplainWhatThisRepresents");
            if (matNeedsToExplainThisToo == null) throw new ArgumentNullException("matNeedsToExplainThisToo");
    
            this.MatNeedsToExplainWhatThisRepresents = matNeedsToExplainWhatThisRepresents;
            this.MatNeedsToExplainThisToo = matNeedsToExplainThisToo;
        }
    
        [Obsolete]
        public override bool Equals(object obj)
        {
            return Equals(obj as MatPair);
        }
    
        public bool Equals(MatPair matPair)
        {
            return matPair != null
                   && matPair.MatNeedsToExplainWhatThisRepresents == MatNeedsToExplainWhatThisRepresents
                   && matPair.MatNeedsToExplainThisToo == MatNeedsToExplainThisToo;
        }
    
        public override int GetHashCode()
        {
            unchecked
            {
                return MatNeedsToExplainWhatThisRepresents.GetHashCode() * 31
                    ^ MatNeedsToExplainThisToo.GetHashCode();
            }
        }
    
        public override string ToString()
        {
            return "{" + MatNeedsToExplainWhatThisRepresents + ", "
                + MatNeedsToExplainThisToo + "}";
        }
    }