gpt4 book ai didi

c# - 我应该使用 IEquatable 来简化工厂测试吗?

转载 作者:太空狗 更新时间:2023-10-29 23:13:39 29 4
gpt4 key购买 nike

我经常使用代表工厂生产的实体的类。为了方便地测试我的工厂,我通常实现 IEquatable<T> , 同时也覆盖 GetHashCodeEquals (如 MSDN 所建议)。

例如;采用以下为示例目的而简化的实体类。通常我的类(class)有更多的属性。偶尔还有一个收藏,在Equals我使用 SequenceEqual 检查的方法.

public class Product : IEquatable<Product>
{
public string Name
{
get;
private set;
}

public Product(string name)
{
Name = name;
}

public override bool Equals(object obj)
{
if (obj == null)
{
return false;
}

Product product = obj as Product;

if (product == null)
{
return false;
}
else
{
return Equals(product);
}
}

public bool Equals(Product other)
{
return Name == other.Name;
}

public override int GetHashCode()
{
return Name.GetHashCode();
}
}

这意味着我可以像这样进行简单的单元测试(假设构造函数在别处进行了测试)。

[TestMethod]
public void TestFactory()
{
Product expected = new Product("James");

Product actual = ProductFactory.BuildJames();

Assert.AreEqual(expected, actual);
}

然而,这引发了一些问题。

  1. GetHashCode并未实际使用,但我已经花时间实现它。
  2. 我实际上很少想使用 Equals在我超出测试的实际应用程序中。
  3. 我花更多时间编写更多测试以确保 Equals实际上工作正常。
  4. 我现在有另外三种方法可以维护,例如向类添加属性,更新方法。

但是,这确实给了我一个非常整洁的 TestMethod .

这是对 IEquatable 的恰当使用吗? ,还是我应该采取另一种方法?

最佳答案

这是否是个好主意实际上取决于您的工厂创建的类型。有两种类型:

  • Types with value semantics(简称值类型)和

  • Types with reference semantics(简称引用类型。)

在 C# 中,通常使用 struct 来表示值类型,使用 class 来表示引用类型,但您不必这样做,您可以使用 class 两者。重点是:

  • 值类型是小型的、通常不可变的、自包含的对象,其主要目的是包含某个值,而

  • 引用类型是具有复杂可变状态、可能引用其他对象和重要功能(即算法、业务逻辑等)的对象。

如果您的工厂正在创建一个值类型,那么当然可以,继续将其设为 IEquatable 并使用这个巧妙的技巧。但在大多数情况下,我们不会为值类型使用工厂,它们往往相当琐碎,我们为引用类型使用工厂,这往往相当复杂,所以如果你的工厂正在创建引用类型,那么实际上,这些各种对象只能通过引用进行比较,因此添加 Equals()GetHashCode() 方法可能会误导甚至错误。

从 HashMap 发生的事情中得到一个提示:Equals()GetHashCode() 在一个类型中的存在通常意味着您可以使用它的一个实例类型作为 HashMap 中的键;但是如果对象不是不可变的值类型,那么它的状态可能会在它被放入映射后发生变化,在这种情况下 GetHashCode() 方法将开始评估其他东西,但哈希map 永远不会为了在 map 中重新定位对象而重新调用 GetHashCode()。这种情况下的结果往往是困惑。

所以,最重要的是,如果您的工厂创建了复杂的对象,那么您或许应该采取不同的方法。显而易见的解决方案是调用工厂,然后检查返回对象的每个属性以确保它们都符合预期。

我或许可以对此提出改进建议,但请注意,我只是想到它,我从未尝试过,因此在实践中它可能是也可能不是一个好主意。在这里:

您的工厂可能会创建实现特定接口(interface)的对象。 (否则,拥有工厂有什么意义,对吗?)因此,理论上您可以规定新创建的实现此接口(interface)的对象实例应该将某些属性初始化为一组特定的值。这将是接口(interface)强加的规则,因此您可以将一些函数绑定(bind)到接口(interface)来检查这是否为真,并且这个函数甚至可以用一些提示进行参数化,以在不同情况下期望不同的初始值。

(上次我检查过,在 C# 中绑定(bind)到接口(interface)的方法通常是扩展方法;我不记得 C# 是否允许静态方法成为一部分接口(interface),或者 C# 的设计者是否已经向该语言中添加了与 Java 的默认接口(interface)方法一样简洁优雅的东西。)

因此,使用扩展方法,它可能看起来像这样:

public boolean IsProperlyInitializedInstance( this IProduct self, String hint )
{
if( self.Name != hint )
return false;
//more checks here
return true;
}

IProduct product = productFactory.BuildJames();
Assert.IsTrue( product.IsProperlyInitializedInstance( hint:"James" ) );

关于c# - 我应该使用 IEquatable 来简化工厂测试吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33988487/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com