Value objects and operator overloading in c#

Value objects are small objects which are not equal by ref, but by value. They do not have an identity. Comparison of two objects is based on a value, not all values in the Value object have to be equal.

Another attribute of a Value object is that it is immutable.

An example of a Value object is a price.

public class Price
{
    private readonly double _value;
    public Price(double value)
    {
        _value = value;
    }

    public double Value {
        get { return _value; }
    }
}

This is a simple value object which holds one property, Value. This Value property holds the actual price.
The value is set by the constructor and stored in a readonly variable. This makes it immutable.

When validation is required, this can be added to the constructor. When a not valid value is provided as argument, an exception should be thrown. It is the objects responsibility to make sure it is valid. A best practice is to throw custom exceptions, f.e. when a negative value is provided, an InvalidPriceException could be thrown. This specific exception can be caught on a higher level which knows how to handle these exceptions.

Comparing two Price objects with the same value but a different ref, will result in false.
It can only be compared by value by using the Value property. This means that we have to understand how the Price value object works in order to compare it. This is not a best practice. It is the responsibility of the value object to determine if it is equal to another value object of the same type.
This is when operator overloading comes to the rescue! Operator overloading allows us to override the default implementation of the operators used on the value object.

Lets start with implementing comparison.

public static bool operator ==(Price x, Price y) 
{
    if ((object)x == null || (object)y == null)
    {
        return false;
    }
 
    return x.Value == y.Value;
}
 
public static bool operator !=(Price x, Price y)
{
    return !(x == y);
}

When implementing the == operator, you are required to implement the != operator.
Do not forget to override the Equals method. By default, this method compares objects by ref. It should return the same result as the == and != operators.

public override bool Equals(System.Object obj)
{
    Price p = obj as Price;
    if ((object)p == null)
    {
        return false;
    }
 
    return Value == p.Value;
}

public override int GetHashCode()
{
    return Value.GetHashCode();
}

Now that we can compare Prices, the next step is to have some simple math functions like adding, subtracting, multiplying and dividing. The math operations is also the responsibility of the value object.


public static Price operator +(Price x, Price y)
{
    return new Price(x.Value + y.Value);
}

public static Price operator -(Price x, Price y)
{
    return new Price(x.Value - y.Value);
}

Remember to return a new instance of your object. This will prevent strange side effects when objects are set to null and the ref is destroyed.
Operator overloading is not limited to the currenty type, it can be implemented for any type. For multiply and divide a double is used.

public static Price operator *(Price x, double y)
{
    return new Price(x.Value * y);
}

public static Price operator /(Price x, double y)
{
    return new Price(x.Value / y);
}

These are some very simple math operations on a Price value object. The key point is that the value object itself is responsible for handling all the operations.
Although this is very basic, it is a perfect scenario for some unit tests.

The following two tabs change content below.
I'm a software developer from Utrecht. Interested in DDD, continuous delivery, new technologies & frameworks.

Latest posts by Vincent Keizer (see all)

Leave a Reply

Your email address will not be published. Required fields are marked *