`EqualOperator()` 和 `NotEqualOperator()` 方法在此 `ValueObject` 实现中如何工作(Microsoft 文档)?
How do the `EqualOperator()` and `NotEqualOperator()` methods work in this `ValueObject` implementation (Microsoft Docs)?
在领域驱动设计中,我们介绍了 ValueObject
的概念,其中对象不携带身份。
Microsoft 在他们的微服务系列中有 provided an implementation of their ValueObject
,它们覆盖了 Equals()
,因此两个具有相同值的 ValueObject
被认为是相同的。
我在下面包含了它们的实现,但我的问题与 EqualOperator()
和 NotEqualOperator()
方法有关 - 这是如何工作的?他们什么时候被调用?
我很熟悉 operator overloads,但这似乎是我以前从未见过的实现,而且我找不到任何相关文档。
实现如下:
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;
}
}
实际上,我发现微软使用 class 实现值类型令人惊讶。通常,对于此目的,结构要好得多,除非您的值对象变得非常大。对于坐标或颜色等值类型的大多数用法,情况并非如此。
撇开这个讨论不谈,这里发生的事情如下:如果你实现一个值对象,你需要正确地实现 Equals
和 GetHashCode
,这包括彼此一致。然而,这两种方法实际上并不难实现,但实现起来很冗长:您需要强制转换对象,然后检查它的每个属性。如果您使用 classes,您有一个额外的样板因素,即您通常希望使用引用相等性检查来加速相等性检查。也就是说,两个对象不必 相同 就 相同,但如果它们 相同 ,那么它们也相等。
您在此处描述的 class 试图通过抽象出相当多的共性来支持使用 classes 的值对象的一致性问题和样板问题。您需要提供的只是构成身份的字段。在大多数情况下,这些只是所有字段。您使用 co-method.
迭代它们
现在,对于实际调用 EqualOperator
和 NotEqualOperator
的实际问题,我猜想它们只是帮助函数,使运算符的实现更容易:您将提供重载==
运算符简单地 returns EqualOperator
和 !=
简单地 returns NotEqualOperator
。你可能会问为什么值类型 base class 没有这些运算符?好吧,我想这是因为这意味着编译器将允许您使用重载运算符将 ==
和 !=
应用于不同类型的值对象。
在领域驱动设计中,我们介绍了 ValueObject
的概念,其中对象不携带身份。
Microsoft 在他们的微服务系列中有 provided an implementation of their ValueObject
,它们覆盖了 Equals()
,因此两个具有相同值的 ValueObject
被认为是相同的。
我在下面包含了它们的实现,但我的问题与 EqualOperator()
和 NotEqualOperator()
方法有关 - 这是如何工作的?他们什么时候被调用?
我很熟悉 operator overloads,但这似乎是我以前从未见过的实现,而且我找不到任何相关文档。
实现如下:
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;
}
}
实际上,我发现微软使用 class 实现值类型令人惊讶。通常,对于此目的,结构要好得多,除非您的值对象变得非常大。对于坐标或颜色等值类型的大多数用法,情况并非如此。
撇开这个讨论不谈,这里发生的事情如下:如果你实现一个值对象,你需要正确地实现 Equals
和 GetHashCode
,这包括彼此一致。然而,这两种方法实际上并不难实现,但实现起来很冗长:您需要强制转换对象,然后检查它的每个属性。如果您使用 classes,您有一个额外的样板因素,即您通常希望使用引用相等性检查来加速相等性检查。也就是说,两个对象不必 相同 就 相同,但如果它们 相同 ,那么它们也相等。
您在此处描述的 class 试图通过抽象出相当多的共性来支持使用 classes 的值对象的一致性问题和样板问题。您需要提供的只是构成身份的字段。在大多数情况下,这些只是所有字段。您使用 co-method.
迭代它们现在,对于实际调用 EqualOperator
和 NotEqualOperator
的实际问题,我猜想它们只是帮助函数,使运算符的实现更容易:您将提供重载==
运算符简单地 returns EqualOperator
和 !=
简单地 returns NotEqualOperator
。你可能会问为什么值类型 base class 没有这些运算符?好吧,我想这是因为这意味着编译器将允许您使用重载运算符将 ==
和 !=
应用于不同类型的值对象。