Learn how you can use value objects in C# to improve the clarity, structure, and maintainability of your code. Credit: David Lofink In the C# programming language, an object can be either a value type or a reference type. While a variable of a value type contains its value, a variable of a reference type contains a reference to an object, or a reference to an instance of the type. The key distinction between a value type and a reference type is in the assignment semantics. Additionally, because a value type is always “copied by value,” when you pass a value type as a parameter to a method or assign it to a variable, the entire data is copied. By contrast, when you assign a reference type, only the reference is copied; both references point to the same object in the memory. C# also lets us create what’s called value objects, a special type of object used in domain-driven design that allows us to articulate domain concepts simply, clearly, and concisely. In this article we will examine value objects, discuss their benefits, and illustrate how we can use them in C#. Create a console application project in Visual Studio First off, let’s create a .NET Core console application project in Visual Studio. Assuming Visual Studio 2022 is installed in your system, follow the steps outlined below to create a new .NET core console application project. Launch the Visual Studio IDE. Click on “Create new project.” In the “Create new project” window, select “Console App (.NET Core)” from the list of templates displayed. Click Next. In the “Configure your new project” window, specify the name and location for the new project. Click Next. In the “Additional information” window, choose “.NET 8.0 (Long Term Support)” as the framework version you want to use. Leave the “Do not use top-level statements” and the “Enable native AOT publish” check boxes unchecked. Click Create. We’ll use this .NET 8 console application project to work with value objects in the subsequent sections of this article. What are value objects in C#? In the C# language, a value object is defined as an object that can be identified only by the state of its properties. Value objects represent a descriptive aspect of a domain without having an identity. In other words, value objects represent elements of the design that we care about only in terms of their contents, not their identities. In domain-driven design (DDD), the value object pattern represents objects without identity and supports equality based on their attributes. It should be noted here that contrary to value objects, entity objects have distinct identities. Unlike entity objects, which are defined by their identity and have mutable state, value objects are defined by their attributes and are immutable. Immutability means you cannot change a value object once it has been created; any operations performed on value objects will create a new instance instead. The immutability of value objects has important performance benefits. Because two value objects are considered equal if they contain identical values, even if they might be different objects, they become interchangeable. In other words, immutability enables object reuse. A value objects example in C# Consider the following class named Address, which contains a few properties and an argument constructor. public class Address { public string HouseNo { get; set; } public string Street { get; set; } public string ZipCode { get; set; } public string City { get; set; } public Address(string houseno, string street, string zipCode, string city) { HouseNo = houseno; Street = street; ZipCode = zipCode; City = city; } } You can create an instance of this class and assign values to its properties using the following code. Address address = new Address("505", "Woodland Street", "CR2 8EN", "London"); However, because the Address class here exposes public setters, you could easily change the values of its properties from outside the class. This violates one of the basic features of value objects, namely its support for immutability. We can fix this by redesigning the Address class as shown below. public class Address { public string HouseNo { get; private set; } public string Street { get; private set; } public string ZipCode { get; private set; } public string City { get; private set; } public Address(string houseno, string street, string zipCode, string city) { HouseNo = houseno; Street = street; ZipCode = zipCode; City = city; } } As you can see above, the properties of our new Address class contain private setters, which do not allow changes to the value of any of these properties from outside the class, thereby preserving immutability. If you execute this program, you will be greeted with the error shown in Figure 1 below. IDG Figure 1. Value objects are immutable. You can also implement value objects using records in C#. To do this, you use the record keyword to define a record type that encapsulates data much the same way we did with the Author class earlier. The following code snippet shows how you can define a record type in C#. public record Address(string Houseno, string Street, string ZipCode, string City); Best practices for using value objects in C# When working with value objects in C#, you should follow these best practices: Implement proper equality checks based on value to prevent inconsistencies and errors. Use value objects to represent concepts in the domain model only when you feel it is appropriate. Use value objects judiciously because they have performance overheads. Consider implementing validation logic within your value object constructor to enforce business rules and constraints when the value object is created. Adhere to recommended patterns and naming conventions to ensure clarity, consistency, and readability in your value object implementations. Value objects encapsulate primitive types and have two main properties, i.e., they do not have an identity and they are immutable. Value objects can simplify complex data structures by encapsulating related data into a single unit. This ensures data integrity, enforces strong typing, reduces errors, and improves the readability of the source code. You can take advantage of value objects to enhance the clarity and maintainability of your C# code by providing a more expressive representation of the domain concepts. Typically, creating value objects in C# involves overriding equality comparison and implementing appropriate value-based semantics. A typical practice involves overriding the Equals() and GetHashCode() methods, and providing other operators for comparison. Typically, we use a base class that comprises these methods that all value object implementations should extend. In the above example, I’ve provided a basic implementation of value objects in C#. In a future post here, I’ll explore how we can implement a value object base class and discuss more advanced concepts. Related content news Spin 3.0 supports polyglot development using Wasm components Fermyon’s open source framework for building server-side WebAssembly apps allows developers to compose apps from components created with different languages. By Paul Krill Nov 18, 2024 2 mins Microservices Serverless Computing Development Libraries and Frameworks how-to How to use DispatchProxy for AOP in .NET Core Take advantage of the DispatchProxy class in C# to implement aspect-oriented programming by creating proxies that dynamically intercept method calls. By Joydip Kanjilal Nov 14, 2024 7 mins Microsoft .NET C# Development Libraries and Frameworks news Microsoft’s .NET 9 arrives, with performance, cloud, and AI boosts Cloud-native apps, AI-enabled apps, ASP.NET Core, Aspire, Blazor, MAUI, C#, and F# all get boosts with the latest major rev of the .NET platform. By Paul Krill Nov 12, 2024 4 mins C# Generative AI Microsoft .NET feature Can Wasm replace containers? WebAssembly revolutionized browser apps, and promises to upend the server stack. How will it impact containers and Kubernetes? Six experts weigh in. By Bill Doerrfeld Nov 11, 2024 12 mins Containers Kubernetes Cloud Native Resources Videos