.NET字典/IDictionary的Equals()契约与Java映射的Equals()契约
怀念.NET字典/IDictionary的Equals()契约与Java映射的Equals()契约,java,.net,collections,immutability,equality,Java,.net,Collections,Immutability,Equality,怀念集合。unmodifiableMap(),我一直在基于实现一个只读IDictionary包装器,我的单元测试很快遇到了一个问题: Assert.AreEqual (backingDictionary, readOnlyDictionary); 失败,即使键值对匹配。我又玩了一点,看起来至少(谢谢西蒙尼) Assert.arequals(backingDictionary,新字典{/*相同内容*/}); 确实过去了 我快速浏览了文档和文档,但令我惊讶的是,我找不到任何Java契约的等价物,
集合。unmodifiableMap()
,我一直在基于实现一个只读IDictionary
包装器,我的单元测试很快遇到了一个问题:
Assert.AreEqual (backingDictionary, readOnlyDictionary);
失败,即使键值对匹配。我又玩了一点,看起来至少(谢谢西蒙尼)
Assert.arequals(backingDictionary,新字典{/*相同内容*/});
确实过去了
我快速浏览了文档和文档,但令我惊讶的是,我找不到任何Java契约的等价物,即两个映射
具有相等的entrySet()s
必须相等。(文档说字典--
不是IDictionary
--覆盖等于()
,但没有说明覆盖的作用。)
因此,C#中的键值相等似乎是字典
具体类的属性,而不是IDictionary
接口的属性。是这样吗?整个系统.集合框架通常都是这样吗
如果是这样的话,我会有兴趣阅读一些关于MS为什么选择这种方法的讨论,以及在C#中检查集合内容是否相等的首选方法
最后,我不介意指针指向经过良好测试的ReadOnlyDictionary
实现:
预计到达时间:说清楚点,我不是在寻找关于如何测试我的实现的建议——这相对来说是微不足道的。我正在寻找关于这些测试应该执行什么合同的指导。为什么
ETA:各位,我知道IDictionary
是一个接口,我知道接口不能实现方法。在Java中也是如此。尽管如此,JavaMap
接口记录了对equals()
方法的期望。当然,一定有.NET接口可以做这样的事情,即使集合接口不在其中 重写equals通常只对具有一定程度值语义的类(例如string
)执行。对于大多数引用类型和良好的默认值,人们更关心的是引用相等,尤其是在不太清楚的情况下(两个字典具有完全相同的键值对,但具有不同的相等比较器[因此添加相同的额外键值对可能使它们现在不同]相等还是不相等?)或者在不经常寻找价值平等的地方
毕竟,您正在寻找两种不同类型被视为相等的情况。平等覆盖可能仍然会使您失败
更重要的是,您始终可以足够快地创建自己的相等比较器:
public class SimpleDictEqualityComparer<TKey, TValue> : IEqualityComparer<IDictionary<TKey, TValue>>
{
// We can do a better job if we use a more precise type than IDictionary and use
// the comparer of the dictionary too.
public bool Equals(IDictionary<TKey, TValue> x, IDictionary<TKey, TValue> y)
{
if(ReferenceEquals(x, y))
return true;
if(ReferenceEquals(x, null) || ReferenceEquals(y, null))
return false;
if(x.Count != y.Count)
return false;
TValue testVal = default(TValue);
foreach(TKey key in x.Keys)
if(!y.TryGetValue(key, out testVal) || !Equals(testVal, x[key]))
return false;
return true;
}
public int GetHashCode(IDictionary<TKey, TValue> dict)
{
unchecked
{
int hash = 0x15051505;
foreach(TKey key in dict.Keys)
{
var value = dict[key];
var valueHash = value == null ? 0 : value.GetHashCode();
hash ^= ((key.GetHashCode() << 16 | key.GetHashCode() >> 16) ^ valueHash);
}
return hash;
}
}
}
公共类SimpleDictEqualityComparer:IEqualityComparer
{
//如果我们使用比IDictionary和use更精确的类型,我们可以做得更好
//字典的比较器也是。
公共布尔等于(IDictionary x,IDictionary y)
{
if(ReferenceEquals(x,y))
返回true;
if(ReferenceEquals(x,null)| | ReferenceEquals(y,null))
返回false;
如果(x.Count!=y.Count)
返回false;
TValue testVal=默认值(TValue);
foreach(x.Keys中的TKey键)
如果(!y.TryGetValue(key,out testVal)| |!等于(testVal,x[key]))
返回false;
返回true;
}
public int GetHashCode(IDictionary dict)
{
未经检查
{
int hash=0x15051505;
foreach(dict.Keys中的TKey键)
{
var值=dict[键];
var valueHash=value==null?0:value.GetHashCode();
hash^=((key.GetHashCode()>16)^valueHash);
}
返回散列;
}
}
}
这并不适用于所有可能需要比较字典的情况,但这就是我的观点
用“可能是他们的意思”相等方法填充BCL将是一个麻烦,而不是一个帮助。我建议使用NUnit的CollectionAssert.arequivalent()。Assert.AreEqual()实际上并不适用于集合 公共密封类词典比较程序
:相等比较
{
公共覆盖布尔等于(
i词典x,i词典y)
{
if(object.ReferenceEquals(x,y))返回true;
if((x==null)| |(y==null))返回false;
如果(x.Count!=y.Count)返回false;
foreach(x中的KeyValuePair kvp)
{
t值y值;
如果(!y.TryGetValue(kvp.Key,out-yValue))返回false;
如果(!kvp.Value.Equals(yValue))返回false;
}
返回true;
}
公共覆盖int GetHashCode(IDictionary obj)
{
未经检查
{
int hash=1299763;
foreach(obj中的KeyValuePair kvp)
{
int keyHash=kvp.Key.GetHashCode();
如果(keyHash==0)keyHash=937;
int valueHash=kvp.Value.GetHashCode();
如果(valueHash==0)valueHash=318907;
散列+=(键散列*值散列);
}
返回散列;
}
}
}
因此,它看起来像是中的键值相等
C#是字典的一个属性
混凝土类,非IDictionary类
接口。是这样吗?它是
总的来说是这样的
系统。集合框架
如果是的话,我想读一些
讨论微软为什么选择这一点
接近
我认为这很简单-IDictionary
是一个接口,接口不能有任何实现,在.NET世界中,两个对象的相等是通过Equals
方法定义的。因此,不可能覆盖IDictionary接口的Equals
,以允许它具有“键值相等性”。您所做的
public class SimpleDictEqualityComparer<TKey, TValue> : IEqualityComparer<IDictionary<TKey, TValue>>
{
// We can do a better job if we use a more precise type than IDictionary and use
// the comparer of the dictionary too.
public bool Equals(IDictionary<TKey, TValue> x, IDictionary<TKey, TValue> y)
{
if(ReferenceEquals(x, y))
return true;
if(ReferenceEquals(x, null) || ReferenceEquals(y, null))
return false;
if(x.Count != y.Count)
return false;
TValue testVal = default(TValue);
foreach(TKey key in x.Keys)
if(!y.TryGetValue(key, out testVal) || !Equals(testVal, x[key]))
return false;
return true;
}
public int GetHashCode(IDictionary<TKey, TValue> dict)
{
unchecked
{
int hash = 0x15051505;
foreach(TKey key in dict.Keys)
{
var value = dict[key];
var valueHash = value == null ? 0 : value.GetHashCode();
hash ^= ((key.GetHashCode() << 16 | key.GetHashCode() >> 16) ^ valueHash);
}
return hash;
}
}
}
public sealed class DictionaryComparer<TKey, TValue>
: EqualityComparer<IDictionary<TKey, TValue>>
{
public override bool Equals(
IDictionary<TKey, TValue> x, IDictionary<TKey, TValue> y)
{
if (object.ReferenceEquals(x, y)) return true;
if ((x == null) || (y == null)) return false;
if (x.Count != y.Count) return false;
foreach (KeyValuePair<TKey, TValue> kvp in x)
{
TValue yValue;
if (!y.TryGetValue(kvp.Key, out yValue)) return false;
if (!kvp.Value.Equals(yValue)) return false;
}
return true;
}
public override int GetHashCode(IDictionary<TKey, TValue> obj)
{
unchecked
{
int hash = 1299763;
foreach (KeyValuePair<TKey, TValue> kvp in obj)
{
int keyHash = kvp.Key.GetHashCode();
if (keyHash == 0) keyHash = 937;
int valueHash = kvp.Value.GetHashCode();
if (valueHash == 0) valueHash = 318907;
hash += (keyHash * valueHash);
}
return hash;
}
}
}
Assert.AreEqual (backingDictionary, readOnlyDictionary);