【问题标题】:How do the `EqualOperator()` and `NotEqualOperator()` methods work in this `ValueObject` implementation (Microsoft Docs)?`EqualOperator()` 和 `NotEqualOperator()` 方法如何在此 `ValueObject` 实现 (Microsoft Docs) 中工作?
【发布时间】:2018-08-18 03:27:00
【问题描述】:

在领域驱动设计中,我们被引入了ValueObject 的概念,其中对象不携带身份。

Microsoft 在其微服务系列中有provided an implementation of their ValueObject,它们覆盖Equals(),因此具有相同值的两个ValueObject 被认为是相同的。

我在下面包含了它们的实现,但我的问题与 EqualOperator()NotEqualOperator() 方法有关 - 这是如何工作的?什么时候叫他们?

我对@9​​87654322@ 很熟悉,但这似乎是我以前从未见过的实现,我找不到任何关于它的文档。

这里是实现:

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

【问题讨论】:

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


    【解决方案1】:

    实际上,令我惊讶的是,Microsoft 使用类实现了值类型。通常,结构体更适合此目的,除非您的值对象变得非常大。对于坐标或颜色等值类型的大多数用法,情况并非如此。

    抛开这个讨论不谈,这里发生的事情如下:如果你实现一个值对象,你需要正确地实现EqualsGetHashCode,它们相互一致。然而,这两种方法实际上并不难,但实现起来很冗长:您需要转换对象,然后检查它的每个属性。如果您使用的是类,那么您有一个额外的样板因素,即您通常希望使用引用相等检查来加速相等检查。也就是说,两个对象不必相同相等,但如果它们相同,那么它们也是相等的。

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

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

    【讨论】:

    • 结构体有缺点 - 无法隐藏默认构造函数,无法继承,在 Entity Framework
    • 我同意你关于结构的观点,但我认为@guillaume31 在这个例子中一针见血,因为它支持 EF。感谢您的回复,这正是我需要知道的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-15
    • 2022-01-16
    • 2015-03-20
    • 1970-01-01
    • 2019-02-10
    • 2011-04-14
    相关资源
    最近更新 更多