.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中也是如此。尽管如此,Java
Map
接口记录了对
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);