如果我实现 IEquatable<T>,我会失去通过引用进行比较的选项吗?

If I implement IEquatable<T>, will I lose the option to compare by reference?

我想将一个对象与另一个对象进行比较,以了解它们是否相等。所以看起来这样做的方法是在我的 class.

中实现 IEquatable 接口

但我不确定这对 class 的行为有何影响。现在,在我的代码中,我用这种方式通过引用比较两个对象:

if(myObject1 == myObject2)
{
    // code when both objects are the same. 
    // Set values in some properties and do some actions according that.
}
else
{
    // code when both objects are no the same. 
    // Set values in some properties and do some actions according that.
}

但在某些特殊情况下,主要是测试,我想比较2个对象,如果所有属性都相等,则考虑相等,但在这种情况下我不知道它是否会影响我的主要代码,其中我比较参考。

另一个选择是实现一个以这种方式进行比较的方法,但我不知道这是个好主意还是实现 IEquatable 接口更好。

谢谢。

这里发生了几件不同的事情。

首先是IEquatable<T>==运算符没有直接相关。如果您实现 IEquatable<T>,但不重写 == 运算符,则 == 将继续执行它当前的操作:通过引用比较您的对象。

IEquatable<T> 给你一个 Equals(T) 方法,仅此而已。就其本身而言,它不会影响 Equals(object)(您还需要实施),或 ==!=.

所以让我们假设您确实重载了 == 运算符,以调用我们的 Equals 方法:

public static bool operator ==(Foo left, Foo right) => Equals(left, right);
public static bool operator !=(Foo left, Foo right) => !Equals(left, right);

这仅更改了两个 Foo 实例之间的 == 运算符。你仍然可以写:

if ((object)myObject1 == (object)myObject2))

这将回退到使用 object== 方法,该方法通过引用进行比较。

另一种方法是:

if (ReferenceEquals(myObject1, myObject2))

哪个just does the same thing.


另请注意,很少为 类 实施 IEquatable<T>:这真的没有意义。 类 已经有一个 Equals(object) 方法和一个 GetHashCode() 方法,您需要覆盖这些方法,添加 Equals(T) 方法不会给您太多。

IEquatable<T> 然而对结构很有用:它们还有一个 Equals(object) 方法,你需要重写,但如果你真的调用它,那么你将结束装箱,因为它接受object。如果你在这里实现 IEquatable<T> 那么你 得到一个 Equals(T) 方法,你可以调用它而不用装箱任何东西。


综上所述,我会编写您的代码,因为它旨在在您的应用程序中运行,并在您的测试项目中执行任何特定于测试的内容。这意味着如果您的对象应该通过代码中的引用进行比较,我不会向对象本身添加任何新内容。

在您的测试项目中,您可以编写自己的方法来检查对象的两个实例是否具有相同的属性(作为自定义 bool AreFoosEqual(Foo f1, Foo f2),或作为完整的 IEqualityComparer<Foo>实例)。然后,您可以使它完全满足您的测试需要,而不必担心破坏您的应用程序。

你也可以把你的测试方法写成一系列的断言,它告诉你哪个 属性是不正确的,有什么区别。这可以给你更丰富的测试输出:

public static void AssertFoosEquals(Foo f1, Foo f2)
{
    Assert.AreEqual(f1.Foo, f2.Foo, "Foo property mismatch");
    Assert.AreEqual(f1.Bar, f2.Bar, "Bar property mismtach");
}

当你进行单元测试时 ->

赞:

public void TestSomething()
{
     var expectedValue1 = "SomeExpectedValue";

     var actualValue = instance.Method();

     Assert.Equal(expectedValue1, actualValue);

}

然后你 "simply" 断言你想要查看的属性,如果你 return 一个对象而不是一个值:

public void TestSomething()
{
     var expectedValue1 = "SomeExpectedValue";

     TestableObject subject = instance.Method();

     Assert.Equal(expectedValue1, subject.Somevalue);
}

如果您想要更通用的设置,您可以使用通用流编写一个反射,它查看一个对象的所有属性并尝试将它们与另一个提供的对象匹配。

或者您可以下载已经允许您执行此操作的 nuget 工具包。

我不会覆盖任何功能,只是为了测试。那就是意大利面条代码。 理想情况下,您的代码应该 100% 可通过单元测试进行验证,而无需使用特定的代码部分来增强或辅助您的测试方法。 (除非所述代码仅限于测试项目本身,并且不包含在任何被测试的实际代码中。

如果你想比较相同的对象,但以不同的方式,我建议使用比较器 实现了 IEqualityComparer<T>:

  public class MyClassTestComparer : IEqualityComparer<MyClass> {
    public bool Equals(MyClass x, MyClass y) {
      if (ReferenceEquals(x, y))
        return true;
      else if (null == x || null == y)
        return false;

      return x.Propery1 == y.Property1 && 
             x.Propery2 == y.Property2 &&
             x.ProperyN == y.PropertyN; 
    }

    public int GetHashCode(MyClass obj) {
      return obj == null 
        ? 0 
        : obj.Propery1.GetHashCode() ^ obj.Propery2.GetHashCode();
    }
  }

然后你可以选择合适的比较器

 public static IEqualityComparer<MyClass> MyClassComparer {
   if (we_should_use_test_comparer)
     return new MyClassTestComparer();
   else
     return EqualityComparer<MyClass>.Default;  
 } 

最后if

 if (MyClassComparer.Equals(myObject1, myObject2)) {
   // Equals: by reference or by properties (in test)
 }