这个 `ValueObject` 实现中的 `EqualOperator()` 和 `NotEqualOperator()` 方法是如何工作的?(Microsoft Docs)

9
在领域驱动设计中,我们介绍了价值对象(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;
     }
 }
1个回答

3
实际上,我觉得微软使用类来实现值类型是令人惊讶的。通常,结构体对于这个目的来说更好,除非你的值对象非常大。对于大多数使用值类型的情况,比如坐标或颜色,这并不是问题。
离开这个讨论不谈,这里发生的事情是:如果你实现了一个值对象,你需要正确地实现Equals和GetHashCode方法,其中包括彼此的一致性。但是,实际上,这两种方法并不难,只是有些冗长:你需要强制转换对象,然后检查它的每个属性。如果你使用的是类,你还有一个额外的样板因素,那就是你通常希望通过引用相等检查来加快相等性检查。也就是说,两个对象不需要是相同的才能相等,但如果它们是相同的,则它们也相等。
你所描述的类是为了支持使用类的值对象的这种一致性问题和样板问题而尝试抽象出许多共同点。你只需要提供构成身份的字段。在大多数情况下,这些字段只是所有字段。你可以使用一个协程迭代它们。
现在,关于何时调用EqualOperator和NotEqualOperator的实际问题,我猜它们只是帮助函数,使操作符的实现更加容易:你会提供一个重载的==操作符,它只返回EqualOperator,并且一个!=操作符,它只返回NotEqualOperator。你可能会问为什么值类型基类没有这些运算符?嗯,我猜这是因为这意味着编译器将允许你对不同类型的值对象使用重载运算符的==和!=。

4
结构体有缺点-没有隐藏默认构造函数,无法继承,在Entity Framework < Core 2.0中支持较差等。 - guillaume31
我同意你关于结构体的看法,但我认为@guillaume31在支持EF方面的这个例子中说得很到位。感谢回复,这正是我需要知道的。 - Matt Griffiths
1
在当前版本的文档中,== 运算符调用 Equals 而不是 EqualOperator 方法。 - jasdefer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接