我有一个实现 IEqualityComparer
的类和该类的比较器:
class Foo
{
public int Int { get; set; }
public string Str { get; set; }
public Foo(int i, string s)
{
Int = i;
Str = s;
}
private sealed class FooEqualityComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo x, Foo y)
{
if (ReferenceEquals(x, y)) return true;
if (ReferenceEquals(x, null)) return false;
if (ReferenceEquals(y, null)) return false;
if (x.GetType() != y.GetType()) return false;
return x.Int == y.Int && string.Equals(x.Str, y.Str);
}
public int GetHashCode(Foo obj)
{
unchecked
{
return (obj.Int * 397) ^ (obj.Str != null ? obj.Str.GetHashCode() : 0);
}
}
}
public static IEqualityComparer<Foo> Comparer { get; } = new FooEqualityComparer();
}
Equals
和 GetHashCode
这两个方法通过比较器的实例在 List.Except
中使用。
我的问题是:如何在此比较器上正确实现单元测试?我想检测是否有人在 Foo
中添加了公共(public)属性而不修改比较器,因为在这种情况下比较器变得无效。
如果我这样做:
Assert.That(new Foo(42, "answer"), Is.EqualTo(new Foo(42, "answer")));
这无法检测到添加了新属性,并且该属性在两个对象中不同。
有什么办法吗?
如果可能的话,我们能不能给一个属性加上一个属性,表示这个属性在比较中是不相关的?
最佳答案
您可以使用反射来获取类型的属性,例如:
var knownPropNames = new string[]
{
"Int",
"Str",
};
var props = typeof(Foo).GetProperties(BindingFlags.Public | BindingFlags.Instance);
var unknownProps = props
.Where(x => !knownPropNames.Contains(x.Name))
.Select(x => x.Name)
.ToArray();
// Use assertion instead of Console.WriteLine
Console.WriteLine("Unknown props: {0}", string.Join("; ", unknownProps));
这样,您可以实现一个测试,如果添加了任何属性,该测试就会失败。当然,您必须在开始时向数组添加新属性。由于从性能的角度来看使用反射是一项昂贵的操作,我建议在测试中使用它,如果您需要比较大量对象,则不要在比较器本身中使用它。
另请注意 BindingFlags
参数的使用,以便您可以将属性限制为仅公共(public)属性和实例级别的属性。
此外,您还可以定义一个自定义属性,用于标记不相关的属性。例如:
[AttributeUsage(AttributeTargets.Property)]
public class ComparerIgnoreAttribute : Attribute {}
您可以将其应用于属性:
[ComparerIgnore]
public decimal Dec { get; set; }
此外,您必须扩展发现未知属性的代码:
var unknownProps = props
.Where(x => !knownPropNames.Contains(x.Name)
&& !x.GetCustomAttributes(typeof(ComparerIgnoreAttribute)).Any())
.Select(x => x.Name)
.ToArray();
关于c# - 如何对 IEqualityComparer 进行单元测试?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44945364/