Search code examples
c#iequatable

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


I would like to compare an object with antoher to know if they are equal or not. So it seems the way to do that is implementing the IEquatable interface in my class.

But I am not sure about how this affect to the behaviour of my class. Now, in my code, I use to compare two object by reference in this way:

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.
}

But in some special cases, mainly in testing, I would like to compare 2 objects and considerate equal if all the properties are equal, but in this case I don't know if it will affect to my main code, in which I am compare by reference.

Another option could be implement a method that compare in this way, but I don't know if it is a good idea or it is better to implement the IEquatable interface.

Thanks.


Solution

  • There are several different things going on here.

    The first is that IEquatable<T> is not directly related to the == operator. If you implement IEquatable<T>, but you don't override the == operator, then == will continue to do what it currently does: compare your objects by reference.

    IEquatable<T> gives you an Equals(T) method, and that's it. By itself, it doesn't affect Equals(object) (which you also need to implement), or == and !=.

    So let's assume that you do overload the == operator, to call our Equals method:

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

    This has only changed the == operator between two Foo instances. You can still write:

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

    and that will fall back to using object's == method, which compares by reference.

    Another way to do this is:

    if (ReferenceEquals(myObject1, myObject2))
    

    which just does the same thing.


    Also note that it's rare to implement IEquatable<T> for classes: there's really no point. Classes already have an Equals(object) method and a GetHashCode() method which you need to override, and adding an Equals(T) method doesn't give you much.

    IEquatable<T> is however useful for structs: they also have an Equals(object) method you need to override, but if you actually call it then you're going to end up boxing, since it accepts object. If you implement IEquatable<T> here then you also get an Equals(T) method, which you can call without boxing anything.


    All of that said, I would write your code as it's intended to work in your application, and do any testing-specific stuff in your test project. This means that if your objects should be compared by reference in your code, I wouldn't add anything new to the object itself.

    In your test project, you can write your own method to check whether two instances of your object have the same properties (either as a custom bool AreFoosEqual(Foo f1, Foo f2), or as a full-blown IEqualityComparer<Foo> instance). You can then make this do exactly what your tests need, without worrying about breaking your application.

    You can also write your test method as a series of assertions, which tells you which property is incorrect, and what the difference is. This can give you richer test output:

    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");
    }