我尝试关注MSDN中的Guidelines并提及This great question,但以下情况似乎与预期不符。
我尝试表示类似于FQN的结构,就好像 P2 之前列出了 P1 一样, P2 只会存在于与 P1 相同的集合中。就像范围的工作方式一样。
我有一个类似这样的属性的类。
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+ "]";
}
}
在Dictionary
(字典)中,我使用data
作为键,因此这会使范围看起来像d1.p1.p2
(或者更确切地说是d1的p1的p2,但是您更喜欢想象一下)
Dictionary<data,int> dict = new Dictionary<data,int>();
当d1.p1和另一个d2.p1不同时,我检查了行为,操作正确解析。但是当d1.p1和d2.p1相同且d1和d2的p2不同时,我会观察到以下行为。
data d1 = new data(){ p1="p1", p2="p2" };
data d2 = new data(){ p1="p1", p2="XX" };
dict.add(d1, 0);
dict.add(d2, 1);
dict[d1] = 4;
结果是两个元素都 4
在观察窗口(VS2013)中,我有我的字典键列表给我看,而不是像我通常预期的那样每个索引只有一个键,我的数据对象的每个属性都是单个索引的键。因此,我不确定是否存在问题,或者我只是误解了Watch窗口对象的表示形式。我知道VS将如何显示一个对象,但是,我不确定我希望它如何显示为字典中的一个键。
直接查看每个哈希码后,我注意到它们重复了。然而,词典并不确定索引是否存在。以下是我看到的数据示例。
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
答案 0 :(得分:1)
- 是否正确覆盖了GetHachCode()?
醇>
当然,对于某些“正确”的定义。它可能没有被重写 well ,但它不是一个不正确的实现(因为被认为是相等的类的两个实例将散列到相同的值)。当然有了这个要求,你总是可以从GetHashCode
返回0并且它将是“正确的”。这当然不会好。
那说你的具体实施并不尽如人意。例如,在您的班级中,字符串的顺序很重要。即new data( "A", "B" ) != new data( "B", "A" )
。但是,由于您的GetHashCode
实现是对称的,因此它们总是相等。最好以某种方式打破对称性。例如:
public int GetHashCode()
{
return p1.GetHashCode() ^ ( 13 * p2.GetHashCode() );
}
现在不太可能发生两个不相等的实例的冲突。
- 是否正确覆盖了?
醇>
嗯,它肯定可以改进。例如,第一个空检查是多余的,在第二个比较中转换为object
。整个事情可能会更好地写成:
public bool Equals( object obj )
{
var other = obj as data;
if( other == null ) return false;
return p1 == obj.p1 && p2 == obj.p2;
}
我还删除了对ToString
的调用,因为它没有显着简化代码或使其更具可读性。这也是执行比较的低效方式,因为在比较甚至发生之前你必须构造两个新的字符串。只是直接比较成员为您提供更多的早期机会,更重要的是,更容易阅读(实际的相等实现不依赖于字符串表示)。
- 如果这两种情况都很好/为什么会发生这种情况?
醇>
我不知道,因为您提供的代码不会这样做。它也不会编译。您的data
课程有两个readonly
字段,您无法使用您在上一个代码段中显示的初始化列表分配这些字段。
我只能推测你所看到的行为,因为你在这里展示的任何内容都不会导致所描述的行为。
我能给出的最好建议是确保你的密钥类不可变。 可变类型与Dictionary
不相称。 Dictionary
类不希望对象的哈希码发生变化,因此如果GetHashCode
依赖于类的任何可变部分,那么事情很可能会变得非常混乱。
- 我认为GetHachCode()是一个字典的主要“比较”操作,这总是正确的吗?
醇>
Dictionary
仅使用GetHashCode
作为“寻址”对象的方式(具体而言,哈希码用于标识项目应放入哪个存储桶)。它不直接用作比较。如果确实如此,它只能用它来确定两个对象不相等,它不能用它来确定它们是否相等。
- 关键是对象的字典真正的“索引”是什么?
醇>
我不完全确定你在这里问的是什么,但我倾向于说答案是无关紧要的。物品去的地方并不重要。如果你关心那种事情,你可能不应该使用Dictionary
。
答案 1 :(得分:0)
是否正确覆盖了GetHashCode()?
没有。您允许null
p1
或p2
和null.GetHashCode()
传递NullReferenceException
GetHashCode
null
。要么禁止传递GetHashCode
,要么让int
为空值返回Equals
(零可以)。
你也是不加改变的整体;这意味着您创建的每个包含两个相同值的类都将具有零hashCode。这是一个非常普遍的碰撞;通常会将每个哈希码乘以素数以避免这种情况。
是否正确覆盖了等于?
没有。您链接到的页面是System.Collections.HashTable
使用的非通用System.Collections.Generic.Dictionary
。您使用的是通用IEquatable<T>
,它使用通用IEquatable<data>
。您需要按照您发布的SO问题的已接受答案中的说明实施IEquatable<data>
。
如果没有定义,Equals(System.Object obj)
确实会回退到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 + "}";
}
}
,但不依赖于这种行为。此外,将int转换为字符串以进行比较并不是“智能”。任何时候你觉得你应该写一个评论,把某些东西称为“聪明”,你就是犯了错误。
更好地实施“数据”将是:
{{1}}