Почему Assert.AreEqual() бросает объект перед сравнением?

Я пишу некоторые модульные тесты, и следующее утверждение не выполняется:

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

Если я назову это вместо этого, он преуспеет:

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

Я предположил, что Assert.AreEqual() в конечном итоге вызывает метод Equals() для типа, который он задает, в этом случае Episode.Equals().

Однако под обложками Microsoft.VisualStudio.TestTools.UnitTesting.Assert нашел следующий код (декомпилированный 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...
}

Это означает, что метод AreEqual() отличает как expected, так и actual до object для принудительного использования базового метода Equals(), а не перегрузки, которую я написал в Episode класс. Базовый метод будет просто проверять, совпадают ли ссылки, какими они не являются.

У меня есть два вопроса:

  • Мое объяснение действительно правильно, или я что-то пропустил?
  • Почему инфраструктура хочет принудительно использовать object.Equals(), а не перегрузку этого метода?

Если это актуально, вот мой метод:

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

Ответ 1

Использует object.Equals(object,object), который имеет дело с такими вещами, как:

  • являются ли они одной и той же ссылкой?
  • является либо ссылкой, либо ссылкой null?

а затем продолжает использовать x.Equals(y) после того, как он обработал эти вещи. Он должен передать их object, потому что это то, что принимает object.Equals(object,object). Литье в object также позволяет избежать некоторых осложнений с помощью Nullable<T> (потому что a T? помещается либо в null, либо в стандартную коробку T).

Однако он также может быть реализован как:

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

который обрабатывает Nullable<T>, IEquatable<T>, struct vs class и несколько других сценариев без бокса.

Но: текущая реализация выполняет задание, а случайный ящик - это не конец света (и: бокс даже не проблема, если ваш тип class).

Ответ 2

В вашем коде вам также необходимо переопределить Equals(object other) (и также необходимо переопределить GetHashCode).

Просто добавьте это в свой код

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

Помните о GetHashCode, если два объекта равны, они должны также возвращать одинаковые хэш-коды. Вот краткая диаграмма, которая поможет визуализировать.

enter image description here

Вы можете проверить CaseNote и Patient на аналогичные проблемы.