custom == for Dictionary扩展认为它们'为空

本文关键字:为空 for Dictionary 扩展 custom | 更新日期: 2023-09-27 18:02:04

我有一个(enum: int) AccessOptions,它用于填充Dictionary,以反映索引用户主体名称列表的简单访问属性。

我需要支持单元测试——特别是
    Assert.AreEqual
    <Dictionary<string, AccessOptions>, 
    Dictionary<string, AccessOptions>)

…这很混乱,并调用Dictionary泛型的相等操作符,该操作符实际上只检查参考值——我需要的是确定两个不同的引用对象包含相同的键值,并且这些键的关联值在每种情况下都是匹配的。

因此,我编写了自己的相等操作符,并创建了一个扩展Dictionary的类,以避免在我的项目中搞砸Dictionary对象。在Visual Studio 2016中调试单元测试Assert的上下文中执行此方法时。AreEqual调用,有几件事偏离了方向,我将在下面用注释标记它们。
        public class SecretAccessRuleSet : Dictionary<string, AccessOptions>
{
    public SecretAccessRuleSet() {}
    public SecretAccessRuleSet(int size) : base (size) {}
    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        return obj.GetType() == GetType() && Equals((SecretAccessRuleSet) obj);
    }
    public static bool operator == (SecretAccessRuleSet a, SecretAccessRuleSet b)
    {
        if (ReferenceEquals(a, b))  
        { 
            return true; 
        }
        /* When the below statement executes in debug, I can watch 
         * execution flow from the equality test against (object)a,
         * straight to "return false" -- but execution does not actually
         * return, but appears to jump directly to the foreach statement
         * two blocks down.  It might be important, or just a bug.  
         */
        if (((object)a == null) || ((object)b == null))
        {
            return false;
        }
        if (a.Count != b.Count)
        {
            return false;
        }
        /* Then when we get here, the visualizer highlights a; I advance 
         * one line and it highlights "in", which I assume is wherein an 
         * enumerator is initialized; I advance again and we jump to the 
         * end of the method!  Literally to the end curly brace.  
         */
        foreach (var entry in a)
        {
            AccessOptions bOpt;
            if (!b.TryGetValue(entry.Key, out bOpt)
                || bOpt != entry.Value)
            {
                return false;
            }
        }
        // If we get here, they're equal
        return true;
    }
    public bool Equals(SecretAccessRuleSet other)
    {
        return this == other;
    }
    public static bool operator !=(SecretAccessRuleSet a, SecretAccessRuleSet b)
    {
        return !(a == b);
    }
    public override int GetHashCode()
    {
        return base.GetHashCode();
    }
}

测试结果表明Assert。AreEqual(a, b)调用返回false,但我不太相信两个集合的内容都被求值了,我不明白为什么。

我可能会删除所有这些,并创建一个单独的方法来测试这些,而不重写操作符,但是——这里出了什么问题?

(感谢大家的宝贵时间)

Update:为了指定我忘记的东西,两个集合都是预期的类型,非null,并且都包含2个条目——实际上是相同的2个条目;我期望相等运算符在测试中返回true。

Update 2:我分隔了null检查;"(object) a == null"的计算结果为false,并继续,但"(object) b == null"的计算结果似乎为true,并将执行发送到"返回false"——但是,同样的两个问题,执行实际上并不返回,而是试图枚举第一个…b也不是零。是否存在b是一个有效对象但(object) b中的强制转换操作可能失败的原因?

custom == for Dictionary扩展认为它们'为空

这个等式检查怎么样?在检查了引用相等性、null和键数之后,您可以使用Except来查看其中一个是否包含另一个中没有的键。如果有,它们就不相等。

只有当所有这些都通过时,您才需要查看是否存在两个字典中相同键的值不匹配的情况。

public override bool Equals(object obj)
{
    if (ReferenceEquals(this, obj)) return true;
    var other = obj as SecretAccessRuleSet;
    if (other == null) return false;
    if (Count != other.Count) return false;
    if (Keys.Except(other.Keys).Any()) return false;
    return Keys.All(k => string.Equals(this[k], other[k]));
}

和往常一样,答案比我想的要简单。

字典对象比较器不是显式的大小写不敏感,并且在两个方法之间的键创建中存在大小写不匹配。重定义SecretAccessRuleSet构造函数,用StringComparer调用基构造函数。OrdinalIgnoreCase修复了此问题。

debug-focus-跳转问题是一个已知的在这种情况下困扰单元测试的显示错误;代码并没有真正执行突出显示的行。这使得整件事看起来比实际更奇怪。

经验教训:

  • Post 所有涉及的代码(有人会抓住这个问题,但我认为创建路由的细节超出了问题的范围,我不应该这样做)。

  • 在怀疑核心IDE之前责怪插件——他们经历的测试是不平等的。

谢谢大家。