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

时间:2018-03-09 14:19:32

标签: c# domain-driven-design value-objects

在Domain Driven Design中,我们引入了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;
     }
 }

1 个答案:

答案 0 :(得分:1)

实际上,我发现Microsoft使用类实现了一个值类型是令人惊讶的。通常,结构对于此目的要好得多,除非您的值对象变得非常大。对于大多数值类型的用法,例如坐标或颜色,情况并非如此。

将此讨论放在一边,这里发生的事情如下:如果您实现了一个值对象,则需要正确实现EqualsGetHashCode,其中包含彼此一致的内容。但是,这两种方法实际上并不是很困难,但实现起来很冗长:您需要转换对象然后检查其每个属性。如果您正在使用类,则还有一个额外的样板因素,即您通常希望使用引用相等性检查来加速相等性检查。也就是说,两个对象不必是相同 相等,但如果它们是相同,那么它们也是相等的。

您在此处描述的类是尝试通过抽象出相当多的共性来支持使用类的值对象的此一致性问题和样板问题。您需要提供的只是构成身份的字段。在大多数情况下,这些只是所有领域。您可以使用协同方法迭代它们。

现在,对于实际调用EqualOperatorNotEqualOperator的实际问​​题,我猜它们只是辅助函数,使运算符的实现更容易:你会提供一个重载的{{ 1}}运算符只返回==EqualOperator,只返回!=。您可能会问为什么值类型基类没有这些运算符?好吧,我想这是因为这意味着编译器允许您使用重载运算符将NotEqualOperator==应用于不同类型的值对象。