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中的强制转换操作可能失败的原因?
这个等式检查怎么样?在检查了引用相等性、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之前责怪插件——他们经历的测试是不平等的。
谢谢大家。