Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Create Value Object in C#

0.00/5 (No votes)
1 Sep 2017 1  
How to create a value object (pattern in Domain Driven Design) in C#. Continue reading...

Problem

How to create a value object (pattern in Domain Driven Design) in C#.

Solution

Create a class that abstracts away (encapsulate) ‘data’ in your domain and provide methods to work on it. Below is a class I don’t consider a good Value Object:

public enum GenderType { Male, Female }

    public sealed class Gender1
    {
        private readonly GenderType genderType;

        internal Gender1(GenderType genderType)
        {
            this.genderType = genderType;
        }

        public GenderType GenderType => this.genderType;
    }
        [Fact]
        public void Setting_with_male_returns_Male_for_GenderType_prop()
        {
            var gender = new Gender1(GenderType.Male);
            Assert.Equal(
                expected: GenderType.Male,
                actual: gender.GenderType);
        }

However, the class below is what I do consider a good Value Object:

public sealed class Gender2
    {
        private readonly GenderType genderType;

        internal Gender2(GenderType genderType)
        {
            this.genderType = genderType;
        }

        public bool IsMale => this.genderType == GenderType.Male;
        public bool IsFemale => this.genderType == GenderType.Female;
    }
        [Fact]
        public void Setting_with_male_enum_returns_true_for_IsMale_prop()
        {
            var gender = new Gender2(GenderType.Male);
            Assert.True(gender.IsMale);
        }

Discussion

The difference between the above two Gender classes is that the first one isn’t encapsulating its internal representation, i.e., how it holds the gender information in an enum.

Exposing internal state as properties isn’t the same as encapsulating state, although this is a common misunderstanding. Good encapsulation is when the internals (data/behaviour) are abstracted away from the client by providing domain specific behaviour.

The second Gender class does that by providing IsMale and IsFemale properties. This way, if the internal representation changes, say from enum to string, then the client doesn’t need to change/know:

public sealed class Gender3
    {
        private readonly string genderType;

        internal Gender3(string genderType)
        {
            this.genderType = genderType;
        }

        public bool IsMale =>
            this.genderType.ToUpper() == "MALE" || this.genderType.ToUpper() == "M";

        public bool IsFemale =>
            this.genderType.ToUpper() == "Female" || this.genderType.ToUpper() == "F";
    }

Client code (the Test in this case) doesn’t need to change even though the internal representation of the class has changed.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here