让我烦恼的是,大多数方法都非常重载,而且最重要的是,它们具有通用版本.一个具体的例子是Assert.AreEqual,它有18个重载,其中:
Assert.AreEqual<T>(T t1,T t2)
这种通用方法有什么用?最初,我认为这是一种直接调用IEquatable< T>的方法.等于(T t)方法,但事实并非如此;它将始终调用非泛型版本object.Equals(object other).在编写了很多期望这种行为的单元测试之后,我发现了很难的方法(而不是像我应该的那样预先检查Assert类的定义).
Assert.AreEqual<T>(T t1,T t2) where T: IEquatable<T>
有没有一个很好的理由为什么不这样做?
是的,你没有实现那些没有实现IEquatable< T>的那些类型的泛型方法,但它不是一个很大的损失,因为通过object.Equals(object other)检查相等,所以Assert.AreEqual(object o1,对象o2)已经足够好了.
当前的通用方法是否提供了我没有考虑的优势,或者只是没有人停下来思考它的情况,因为它没有那么多的交易?我看到的唯一优势是参数类型安全,但这似乎有点差.
编辑:修复了一个错误,当我指的是IEquatable时我继续引用IComparable.
解决方法
问题是对于任何未被其自身特定过载覆盖的T,编译器将选择通用的AreEqual< T>.方法作为重载决策期间的最佳拟合,因为它确实通过精确匹配.在该过程的不同步骤中,编译器将评估T通过约束.对于任何未实现IEquatable< T>的T,此检查将失败并且代码将无法编译.
请考虑单元测试库代码的简化示例以及库中可能存在的类:
public static class Assert { public static void AreEqual(object expected,object actual) { } public static void AreEqual<T>(T expected,T actual) where T : IEquatable<T> { } } class Bar { }
Class Bar不在约束中实现接口.如果我们然后将以下代码添加到单元测试中
Assert.AreEqual(new Bar(),new Bar());
由于对最佳候选方法的约束不满意,代码将无法编译. (Bar代替T,这使它成为比对象更好的候选者.)
The type ‘Bar’ cannot be used as type parameter ‘T’ in the generic type or method ‘Assert.AreEqual<T>(T,T)’. There is no implicit reference conversion from ‘Bar’ to ‘System.IEquatable<Bar>’.
为了满足编译器并允许我们的单元测试代码进行编译和运行,我们必须将至少一个输入转换为对象的方法,以便可以选择非泛型重载,对于任何给定的情况都是如此T可能存在于您自己的代码或您希望在未实现接口的测试用例中使用的代码中.
Assert.AreEqual((object)new Bar(),new Bar());所以必须提出这个问题 – 这是理想的吗?如果您正在编写单元测试库,您是否会创建一个具有这种不友好限制的方法?我怀疑你不会,微软单元测试库的实现者(无论是否出于这个原因)也没有.