问题描述
||
我需要使用给定两个实例
d1
和d2
的Dictionary<long,string>
集合,它们分别具有相同的keyvaluePair<long,string>
内容,可以按任何顺序插入:
(d1 == d2)
等于true
d1.GetHashCode()
==d2.GetHashCode()
通过使用ѭ8而不是常规的Dictionary
,最容易实现第一个要求。
第二个需求是必要的,因为我有一点需要存储Dictionary<Dictionary<long,string>,List<string>
-主Dictionary
类型用作另一个Dictionary
的密钥,并且如果HashCodes不能基于相同的内容求值,则使用ContainsKey()
将不起作用我想要的方式(即:如果字典中已经插入了一个以d1
作为键的项目,那么dictionary.ContainsKey(d2)
的值应为true
。
为此,我创建了一个新对象class ComparableDictionary : SortedDictionary<long,string>
,并包括以下内容:
public override int GetHashCode() {
StringBuilder str = new StringBuilder();
foreach (var item in this) {
str.Append(item.Key);
str.Append(\"_\");
str.Append(item.Value);
str.Append(\"%%\");
}
return str.ToString().GetHashCode();
}
在我的单元测试中,这符合相等性和哈希码的标准。但是,在阅读GetHashCode的准则和规则时,遇到了以下问题:
规则:当对象包含在取决于哈希码保持稳定的数据结构中时,GetHashCode返回的整数绝不能更改
使对象的哈希码值可以随着对象字段的变化而发生变化是允许的,尽管很危险。如果您有这样一个对象并将其放在哈希表中,则使该对象发生突变的代码和维护该哈希表的代码必须具有一些已达成共识的协议,以确保该对象在插入时不会发生突变哈希表。该协议的外观取决于您。
如果对象的哈希码在哈希表中时可以发生变化,则显然Contains方法将停止工作。您将对象放入存储桶#5中,对其进行了变异,当您询问集合是否包含变异对象时,它会在存储桶#74中查找而没有找到它。
记住,对象可以以您意想不到的方式放入哈希表。许多LINQ序列运算符在内部使用哈希表。在枚举返回对象的LINQ查询时,不要进行危险的对象变异!
现在,在应设置所有ComparableDictionary
集合内容的地方,code19ѭ仅在代码中使用一次。因此,根据这些准则,我认为可以像我所做的那样覆盖GetHashCode
(完全基于字典的内容)是可以接受的。
在介绍之后,我的问题是:
我知道SortedDictionary
的性能比Dictionary
差(我可以有数百个对象实例化)。使用SortedDictionary
的唯一原因是我可以根据字典的内容进行相等比较的工作,而与插入顺序无关。有没有一种更好的方法可以满足此平等要求而不必使用ѭ8?
根据要求,我对“ 21”的实施是否可以接受?即使它基于可变内容,我也不认为这会带来任何风险,因为它唯一使用的地方(我认为)是在内容设置之后。
注意:虽然我一直使用Dictionary
或SortedDictionary
进行设置,但我不喜欢这些收集类型。主要需求是一个可以存储值对并满足上面定义的相等性和哈希要求的集合。
解决方法
您的ѭ21实施对我来说似乎可以接受,但这不是我会做的。
这是我要做的:
使用组合而不是继承。除了其他方面,继承在平等方面变得奇怪
在字典中使用
Dictionary<TKey,TValue>
变量
通过对各个键/值对哈希码进行异或来实现ѭ21
通过检查大小是否相同来实现相等性,然后检查\“ this \”中的每个键以查看其值在另一个字典中是否相同。
所以像这样:
public sealed class EquatableDictionary<TKey,TValue>
: IDictionary<TKey,TValue>,IEquatable<ComparableDictionary<TKey,TValue>>
{
private readonly Dictionary<TKey,TValue> dictionary;
public override bool Equals(object other)
{
return Equals(other as ComparableDictionary<TKey,TValue>);
}
public bool Equals(ComparableDictionary<TKey,TValue> other)
{
if (ReferenceEquals(other,null))
{
return false;
}
if (Count != other.Count)
{
return false;
}
foreach (var pair in this)
{
var otherValue;
if (!other.TryGetValue(pair.Key,out otherValue))
{
return false;
}
if (!EqualityComparer<TValue>.Default.Equals(pair.Value,otherValue))
{
return false;
}
}
return true;
}
public override int GetHashCode()
{
int hash = 0;
foreach (var pair in this)
{
int miniHash = 17;
miniHash = miniHash * 31 +
EqualityComparer<TKey>.Default.GetHashCode(pair.Key);
miniHash = miniHash * 31 +
EqualityComparer<Value>.Default.GetHashCode(pair.Value);
hash ^= miniHash;
}
return hash;
}
// Implementation of IDictionary<,> which just delegates to the dictionary
}
另请注意,我不记得EqualityComparer<T>.Default.GetHashCode
是否可以处理空值-我怀疑它会这样做,因为返回0表示空值。值得检查:)