Definition of Terms
In the following discussion I will use the term Value Object to refer to an object which:
- expresses only a value, or set of related values
- has no identity
- is typically immutable
This is a standard description of a Value Object as given by Domain Driven Design (see the Value Object definition at domaindrivendesign.org, or see Evans, Domain Driven Design
p. 97ff; cf. Fowler, PoEAA
, front cover and p. 486ff). I will use the term value type to refer to something more specific: any .NET type which derives from System.ValueType. In C#, this means structs, enums, and most of your built-in types (excluding string). A Value Object is a concept or pattern while a value type is a concrete part of the .NET framework. In .NET, a Value Object could be implemented using either a value type (struct) or a reference type (class).
Great, now on to the real stuff...
Aren't Structs Enough?
Martin Fowler in PoEAA has this to say about Value Objects in .NET:
.NET has a first-class treatment of Value Object. In C# an object is marked as a Value Object by declaring it as a struct instead as a class [sic]. The environment then treats it with value semantics.
While it's true that structs do offer built-in value-based equality and hash code, structs have some important limitations you should be aware of before you consider them the absolute solution for all your Value Object implementation needs:
- Structs do not allow inheritance. If you need to create a new Value Object that extends some base object, structs won't allow you to do it. This is not an uncommon situation for me: I may have an Address Value Object that serves as a base international address, and then derive a country-specific address, such as USAddress, from it. I can't do this with structs.
- Structs cannot guarantee construction to a valid state. Structs always include a default, parameterless constructor which initializes every field to its default value (such as null or 0). What this means is that, even if you provide a constructor that takes parameters to fully initialize all of the Value Object's internal fields, there is no guarantee that a caller won't bypass it and use the default constructor instead. Consider an Address Value Object implemented as a struct that expects it's fields to be populated before it can be used; there's nothing stopping someone from simply passing a new Address() to a method in your domain model that expects a correctly-initialized Address which then chokes on a bunch of null data.
- Structs do not offer built-in immutability (and neither to classes). This really isn't a criticism of structs as much as a clarification of Mr. Fowler's advice. Usually, your Value Objects should be immutable. There are exceptions to this (see Evans, Domain Driven Design, p. 101, side column), but generally speaking you want immutability by default. For this reason, I don't consider a struct to be a "first-class treatment" of Value Object implementation. It's as close as you get, but just understand that if you want true immutability, you have to do all of the same grunt-work as you would with a class: readonly fields, get-only properties, and an appropriate constructor to initialize the object.
Luca Bolognese offers even more reasons why structs aren't the ideal solution for implementing Value Objects, but to me the lack of inheritance and construction are the two most significant.
Structs vs. Classes: Lots of Typing Either Way You Slice It
The truth is, neither structs nor classes offer quick-and-easy implementation of immutable Value Objects. They each have their own advantages and disadvantages, but they also both share some similar shortcomings that you'll have to face regardless of which one you use. To illustrate this, here is a comparison of structs and classes with regards to their suitability for Value Object implementation. I've colored aspects of each in red if it makes implementing a Value Object more difficult, and in green if it makes it easier:
| struct |
class |
|
|
|
|
|
|
- does not allow inheritance
|
|
- always includes a default constructor
|
- can require a specific constructor
|
- automatic implementation of Equals and GetHashCode based on value
|
- Equals and GetHashCode must be overridden for value-based equality
|
- must override == and != operators
|
- must override == and != operators
|
- immutable properties require a lot of typing to implement:
- a private, readonly field
- a public get-only property
- a corresponding constructor for initialization
|
- immutable properties require a lot of typing to implement:
- a private, readonly field
- a public get-only property
- a corresponding constructor for initialization
|
So if you use a struct, you gain automatic equality and hash code implementation (though it requires boxing), but you lose inheritance and absolute control of construction. If you use a class, you gain inheritance and and construction control, but then you're faced with having to manually implement value-based equality and hash code. But either way you go, there's a lot of repetitive typing required.
For example, let's say we wanted to define simple Value Object called PersonName that had two string properties: FirstName and LastName. A minimal class-based implementation in C# might look like this:
public class PersonName
{
private readonly string _firstName;
private readonly string _lastName;
public PersonName(string firstName, string lastName)
{
_firstName = firstName;
_lastName = lastName;
}
public string FirstName
{
get { return _firstName; }
}
public string LastName
{
get { return _lastName; }
}
public PersonName NewFirstName(string newFirstName)
{
return new PersonName(newFirstName, LastName);
}
public PersonName NewLastName(string newLastName)
{
return new PersonName(FirstName, newLastName);
}
#region Equality and Hash Code
public override bool Equals(object obj)
{
var temp = obj as PersonName;
if (!object.ReferenceEquals(temp, null))
return this.Equals(temp);
return false;
}
public static bool operator ==(PersonName left, PersonName right)
{
return object.Equals(left, right);
}
public static bool operator !=(PersonName left, PersonName right)
{
return !(left == right);
}
public bool Equals(PersonName other)
{
if (object.ReferenceEquals(other, null))
return false;
else if (object.ReferenceEquals(other, this))
return true;
return (this.FirstName == other.FirstName && this.LastName == other.LastName);
}
public override int GetHashCode()
{
int hashCode = 0;
hashCode = (hashCode * 37) + (FirstName == null ? 0 : FirstName.GetHashCode());
hashCode = (hashCode * 37) + (LastName == null ? 0 : LastName.GetHashCode());
return hashCode;
}
#endregion
}
A struct-based implementation (if we're comfortable with the nature of a struct) is a little better, but not much:
public struct PersonName
{
private readonly string _firstName;
private readonly string _lastName;
public PersonName(string firstName, string lastName)
{
_firstName = firstName;
_lastName = lastName;
}
public string FirstName
{
get { return _firstName; }
}
public string LastName
{
get { return _lastName; }
}
public PersonName NewFirstName(string newFirstName)
{
return new PersonName(newFirstName, LastName);
}
public PersonName NewLastName(string newLastName)
{
return new PersonName(FirstName, newLastName);
}
#region Equality Operators
public static bool operator ==(PersonName left, PersonName right)
{
return object.Equals(left, right);
}
public static bool operator !=(PersonName left, PersonName right)
{
return !(left == right);
}
#endregion
}
All of that just to get an immutable Value Object with two properties. Either way you go, class or struct, it's still a lot of noise.
So What Do We Need?
What we need is an easy way to quickly implement an immutable Value Object. We should simply have to specify the name of our Value Object, the names and types of each of its properties, and all of the boilerplate stuff should be done for us: equality, hash code, a standard constructor for our fields, and optionally some helper methods to create new value objects by specifying a new value for a property (like NewFirstName and NewLastName).
This is a simplification of the problem, though, because in practice there's a lot of little stuff that often needs tweaking. For example, you may need to tweak the Equals or GetHashCode method (say to ignore the case of some of your string-based properties), you may need to do something a lot more complex in your constructor than merely assign values to fields, or you may need to call a base constructor and pass in certain pre-defined values, etc. Ideally, we should have some way of specifying the basic outline of a value object--it's name and properties--have the boilerplate stuff generated for us, but still have the option of stepping in and hand-tuning anything that needs it.
Luca Bolognese's Approach
Luca Bolognese has blogged about much the same thing as I have above, and his quest for quick-and-easy immutable Value Objects in C# led him to creating a library of objects that model functional programming concepts which he borrowed from F#! I actually spent quite a bit of time with Luca's idea, but in the end decided that his library-approach didn't provide the level of fine-grained control I was after. Nevertheless, I learned a lot from Luca and came away with my own version of his library that I'll definitely be using in the future.
My Quest
I've spent a lot of time over the past month in pursuit of an easy way to implement Value Objects in C#. My quest for the Value Object Holy Grail has led me from snippets, to macros, to code generating classes configured by XML and harnessed by text templates in Visual Studio. I'll be blogging about each of them individually in future posts.
Suggestions for the C# Team
It would be nice if we had built-in language support for immutable Value Objects.
A step in the right direction would be to allow readonly as a modifier to property setters. With this, we could reduce the standard immutable property code to a simple public string FirstName { get; readonly set; }. While I've wanted this feature for a while, more is needed to complete the picture: we need the compiler to help us with equality and hash code support.
What if there were just one or two new keywords which we could use to designate to the C# compiler
- that we want equality and hash code automatically implemented for a class, and
- which fields/properties/methods should be used for the equality and hash code computation.
Perhaps something along the lines of:
//The keyword "valueobject" indicates that Equals(object), Equals(PersonName), ==, !=,
//and GetHashCode should all be automatically generated by the compiler based on the use
//of "valueitem" (see below).
public valueobject class PersonName
{
//The keyword "valueitem" indicates that this property
//should be used when calculating equality and hash code
public valueitem string FirstName { get; readonly set; };
//Here this property would *not* be considered for equality/hash code.
//Instead, we'll use a custom, private property (below) so that our equality
//and hash code use a modified version.
public string LastName { get; readonly set; };
//Constructor implementation is still done as usual to give us complete control.
//Note, though, the use of the readonly property setters.
public PersonName(string firstName, string lastName)
{
FirstName = firstName;
LastName = lastName;
}
//Rather than use LastName directly, we instruct the compiler to use this property
//as part of the equality/hash code computation, allowing us to ignore the case
//of the last name.
private valueitem string LastNameValueItem { get { return LastName.ToLower(); } }
}
Then, we could do something like this:
PersonName adamCooper1 = new PersonName("Adam", "Cooper");
PersonName adamCooper2 = new PersonName("Adam", "COOPER");
PersonName johnDoe = new PersonName("John", "Doe");
PersonName adamCooperIncorrectFirstNameCase = new PersonName("adam", "Cooper");
//Writes "false" since these two objects clearly have different values
Console.WriteLine(adamCooper1 == johnDoe);
//Writes "true" since our object ignores last name case
Console.WriteLine(adamCooper1 == adamCooper2);
//Writes "false" since our object requires first names to be identical in case
Console.WriteLine(adamCooper1 == adamCooperIncorrectFirstNameCase);
This is a short example, but you can easily see how powerful just a couple of new keywords and the ability to create readonly property setters would be. I'd love to hear what other folks think about this idea, both inside and outside Microsoft.
The most glaring problem I see is how to "automatically" implement GetHashCode, since the compiler would need to know how to combine a bunch of separate hash codes into a new hash code. I'll leave this to better minds to work out, but it would be nice if there was some way to specify how a class should concatenate two hash codes and have the compiler use that or default to a known-good implementation.
But whether the syntax looks like this or something completely different, it's hard to argue that built-in immutable Value Object support wouldn't be a good thing, particularly with the rise of distributed and parallel computing.
Relevant Resources
- Jonathan Allen's "A Detailed look at Overriding the Equality Operator" - This is an excellent article demonstrating how to implement equality and hash code for both classes and structs in C#. If you need to implement Equals, ==, !=, or GetHashCode by hand and aren't sure what you're doing, make sure you read Jonathan's article first.
- Luca Bolognese's Series on Creating Immutable Value Objects in C# - You'll want to find other resources for the specifics of implementing equality and hash code, but Luca's thoughts on the subject of Value Object implementation in C# are still relevant.
- Luca Bolognese's Series on a C# Library to Write Functional Code - Luca's Value Object series above segued into a new series on functional objects in C# when he had the idea to use an immutable Record class as a base for his Value Objects.
- Eric Evans, Domain Driven Design
- Martin Fowler, Patterns of Enterprise Application Architecture