Reputation: 4854
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:
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);
}
Upvotes: 11
Views: 2578
Reputation: 127603
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.
Upvotes: 4
Reputation: 1063884
It is using object.Equals(object,object)
, which deals with things like:
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
).
Upvotes: 6