Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Assert.AreEqual() cast to object before comparing?

I'm writing some unit tests and the following assertion fails:

Assert.AreEqual(expected.Episode, actual.Episode);

If I call this instead, it succeeds:

Assert.IsTrue(expected.Episode.Equals(actual.Episode));

I had assumed that Assert.AreEqual() ultimately calls the Equals() method for the type it is given, in this case Episode.Equals().

However, under the covers in Microsoft.VisualStudio.TestTools.UnitTesting.Assert I found the following code (decompiled by ReSharper):

public static void AreEqual<T>(T expected, T actual, string message, params object[] parameters)
{
    if (object.Equals((object)expected, (object)actual))
        return;
    Assert.HandleFail...
}

This implies to me that the AreEqual() method is casting both expected and actual to object to force the use of the base Equals() method rather than the overload I have written in the Episode class. The base method will simply check to see if the references are the same, which they are not.

I have two questions:

  1. Is my explanation actually correct, or have I missed something?
  2. Why would the framework want to force use of object.Equals() rather than an overload of that method?

If it's relevant, here is my method:

public bool Equals(Episode other)
{
    return Number == other.Number &&
           CaseNote.Equals(other.CaseNote) &&
           Patient.Equals(other.Patient);
}
like image 243
Sir Crispalot Avatar asked Nov 14 '12 14:11

Sir Crispalot


1 Answers

It is using object.Equals(object,object), which deals with things like:

  • are they the same reference?
  • is either or both a null reference?

and then goes on to use x.Equals(y) after it has handled those things. It has to cast them to object because that is what object.Equals(object,object) takes. Casting to object also avoids some complications with Nullable<T> (because a T? boxes either to null or to a regular boxed T).

However, it could also have been implemented as:

 if (EqualityComparer<T>.Default.Equals(expected,actual))
    return;

which handles Nullable<T>, IEquatable<T>, struct vs class, and a few other scenarios without any boxing.

But: the current implementation does the job, and the occasional box isn't the end of the world (and: boxing isn't even an issue if your type is a class).

like image 171
Marc Gravell Avatar answered Sep 18 '22 12:09

Marc Gravell