简体   繁体   中英

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

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

In your code you need to override Equals(object other) as well (and need to override GetHashCode too).

Just add this to your code

public bool Equals(Episode other)
{
    return Number == other.Number &&
           CaseNote.Equals(other.CaseNote) &&
           Patient.Equals(other.Patient);
}

public override bool Equals(object other)
{
    Episode castOther = other as Episode;
    if(castOther == null)
        return false;
    return this.Equals(castOther);
}

public override int GetHashCode()
{
    //TODO: Implement using the members you used in "Equals(Episode other)"
    throw new NotImplmentedExecption();
}

Remember for GetHashCode if two objects are equal they must also return equal hash codes. Here is a quick diagram to help visualize.

在此输入图像描述

You may want to check CaseNote and Patient for similar issues.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM