【问题标题】:When and why to use a "ValueObject" base class (from the Microsoft Docs) in C#?何时以及为何在 C# 中使用“ValueObject”基类(来自 Microsoft Docs)?
【发布时间】:2020-09-11 20:14:10
【问题描述】:

我正在尝试了解 C# 中 ValueObject 的用例、何时使用以及 ValueObject 的需求。我在文档中看到它可以在我们想要初始化对象然后不想更改意味着使其不可变的属性时使用,但它与单例模式不同,您可以在构造函数中初始化对象属性并且它会在应用程序的整个生命周期内持续存在吗?现在为什么我们需要 ValueObject 以及这个 EqualityComparer 和 hashCode() 等的全部内容。

来自 Microsoft Docs 的代码:

    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> GetEqualityComponents();
    
        public override bool Equals(object obj)
        {
            if (obj == null || obj.GetType() != GetType())
            {
                return false;
            }
    
            var other = (ValueObject)obj;
    
            return this.GetEqualityComponents().SequenceEqual(other.GetEqualityComponents());
        }
    
        public override int GetHashCode()
        {
            return GetEqualityComponents()
                .Select(x => x != null ? x.GetHashCode() : 0)
                .Aggregate((x, y) => x ^ y);
        }
        // Other utility 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; }
    
        public 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> GetEqualityComponents()
        {
            // 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;
        }
    }

在上面的代码中,这个地址是什么,我们为什么使用ValueObject,这个equalOperator是什么,都写在ValueObject类中,只是想了解实时使用ValueObject的用例,为什么我们需要它,它是什么场景使用ValueObjects的实时用例,以及为什么我们需要Equality运算符,而不是valueObject内部的equal运算符。

我是这个话题的新手。

【问题讨论】:

  • 这个“ValueObject”不是 C# 本身的一部分,也不是必需的。在使用过类似基本类型的代码(尽管使用了反射)之后,我可以从经验和意见的交集处保密地说,没有任何单独的观点“不要这样做"。使用工具(ReSharper、最近的 VS?)或使用 TT 或类似工具提供的代码生成。
  • 包含一个指向文档/代码源的链接,这样也可能提供它自己的基本原理。
  • 就“为什么”而言,正如一些原始作者所设想的那样:它减少了代码重复,但代价是引入了人工子类型关系,因为具体类型只需要提供成员的值,通过 GetEqualityComponents 完成。使用具有自动代码生成/更新功能的工具时,这本身不是问题。
  • @VijenderReddyChintalapudi 我认为这个人在Value Object 上写得很好,并给出了一个真实世界使用示例的小想法。 - (enterprisecraftsmanship.com/posts/value-objects-explained)
  • 你不能只使用records 来实现不变性吗?

标签: c# value-objects


【解决方案1】:

ValueObject 与值对象或不可变对象不同。恕我直言 当您使用类似领域驱动设计的方法时,ValueObject 类应该用于业务逻辑。

在 DDD 中,值​​对象不能由 Id 标识,而是由对象中的字段标识,因此需要比较对象中的一个或多个属性的相等运算符。

这也是您无法更改属性的原因,因为该对象将无法与它的其他加载实例进行比较。就像在示例中一样,如果您更改门牌号,它就不再是同一个地址了。

在实现 DDD 值对象时,您必须将所有比较逻辑复制到每个类中,因此需要重复代码。

ValueObject 类消除了这种需求,并使您自己的对象更加以业务为中心(即更具可读性),就像在使用 DDD 时一样(或者只是一个干净的业务层)。

【讨论】:

    猜你喜欢
    • 2018-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-25
    • 2011-04-02
    • 2015-10-13
    相关资源
    最近更新 更多