В области проектирования, ориентированного на предметную область, мы знакомимся с концепцией ValueObject, в которой объекты не имеют идентичности.
У Microsoft есть предоставили реализацию своего ValueObject в своей серии микросервисов, где они переопределяют Equals(), так что два ValueObject с одинаковыми значениями считаются идентичными.
Я включил их реализацию ниже, но мой вопрос касается методов EqualOperator() и NotEqualOperator() - как это работает? когда они называются?
Я знаком с перегрузка оператора, но похоже, что это реализация, которую я раньше не видел, и я не могу найти никакой документации по ней.
Вот реализация:
public abstract class ValueObject
{
protected static bool EqualOperator(ValueObject left, ValueObject right)
{
if (ReferenceEquals(left, null) ^ ReferenceEquals(right, null))
{
return false;
}
return ReferenceEquals(left, null) || left.Equals(right);
}
protected static bool NotEqualOperator(ValueObject left,
ValueObject right)
{
return !(EqualOperator(left, right));
}
protected abstract IEnumerable<object> GetAtomicValues();
public override bool Equals(object obj)
{
if (obj == null || obj.GetType() != GetType())
{
return false;
}
ValueObject other = (ValueObject)obj;
IEnumerator<object> thisValues = GetAtomicValues().GetEnumerator();
IEnumerator<object> otherValues =
other.GetAtomicValues().GetEnumerator();
while (thisValues.MoveNext() && otherValues.MoveNext())
{
if (ReferenceEquals(thisValues.Current, null) ^
ReferenceEquals(otherValues.Current, null))
{
return false;
}
if (thisValues.Current != null &&
!thisValues.Current.Equals(otherValues.Current))
{
return false;
}
}
return !thisValues.MoveNext() && !otherValues.MoveNext();
}
// Other utilility methods
}
Вот пример их использования:
public class Address : ValueObject
{
public String Street { get; private set; }
public String City { get; private set; }
public String State { get; private set; }
public String Country { get; private set; }
public String ZipCode { get; private set; }
private Address() { }
public Address(string street, string city, string state, string country,
string zipcode)
{
Street = street;
City = city;
State = state;
Country = country;
ZipCode = zipcode;
}
protected override IEnumerable<object> GetAtomicValues()
{
// Using a yield return statement to return
// each element one at a time
yield return Street;
yield return City;
yield return State;
yield return Country;
yield return ZipCode;
}
}





На самом деле, я нахожу удивительным, что Microsoft реализовала тип значения с помощью класса. Обычно для этой цели лучше подходят структуры, если только ваши объекты-значения не становятся очень большими. Для большинства случаев использования таких типов значений, как координаты или цвета, это не так.
Оставляя это обсуждение в стороне, здесь происходит следующее: если вы реализуете объект значения, вам необходимо правильно реализовать Equals и GetHashCode, которые последовательно включают друг друга. Однако эти два метода на самом деле не очень сложны, но многословны для реализации: вам нужно преобразовать объект, а затем проверить каждое из его свойств. Если вы используете классы, у вас есть дополнительный шаблонный фактор, который заключается в том, что вы обычно хотите ускорить проверку равенства с помощью проверки равенства ссылок. То есть два объекта не обязательно должны быть одно и тоже, чтобы быть равный, но если они являются одно и тоже, то они также равны.
Представленный здесь класс является попыткой поддержать эту проблему согласованности и шаблонную проблему для объектов-значений, использующих классы, путем абстрагирования некоторых общих черт. Все, что вам нужно предоставить, - это поля, составляющие идентификацию. В большинстве случаев это просто все поля. Вы повторяете их, используя совместный метод.
Теперь, что касается фактического вопроса о том, когда на самом деле вызываются EqualOperator и NotEqualOperator, я бы предположил, что это просто вспомогательные функции, упрощающие реализацию операторов: вы должны предоставить перегруженный оператор ==, который просто возвращает EqualOperator и !=, который просто возвращает NotEqualOperator. Вы можете спросить, почему базовый класс типа значения не имеет этих операторов? Что ж, я думаю, это потому, что это будет означать, что компилятор позволит вам применять == и != к различным типам объектов значений с помощью перегруженного оператора.
Я был бы согласен с вами относительно структур, но я думаю, что @ guillaume31 попал в точку с этим примером в его поддержке EF. Спасибо за ответ, это именно то, что мне нужно было знать.
У структур есть недостатки - нет возможности скрыть конструктор по умолчанию, нет наследования, плохая поддержка в Entity Framework <Core 2.0 и т. д.