Implementing a Value-Object Base class (Supertype pattern–DDD patterns related)


It is usually a recommended practice to have Value-Object base-class so we can have common functionality which can be used by all of our value-object classes. Typically, comparison methods or any other common subject for Value-Objects, should be included here.

Below I show a sample Value-Object Base-Class:

public class ValueObject<TValueObject> : IEquatable<TValueObject>
    where TValueObject : ValueObject<TValueObject>
{
    
public bool Equals(TValueObject other)
{
    if ((object)other == null)
        return false;

    //compare all public properties
    PropertyInfo[] publicProperties = this.GetType().GetProperties();

    if ((object)publicProperties != null
        &&
        publicProperties.Any())
    {
        bool result = true;
        foreach (var item in publicProperties)
        {
            //compare two values using default equatable method
            if (!item.GetValue(this, null).Equals(item.GetValue(other, null)))
            {
                result = false;
                break;
            }
        }

        return result;
    }
    else
        return true;
}

public override bool Equals(object obj)
{
    if ((object)obj == null)
        return false;

    ValueObject<TValueObject> item = obj as ValueObject<TValueObject>;

    if ((object)item != null)
        return Equals((TValueObject)item);
    else
        return false;

}

public override int GetHashCode()
{
    int hashCode = 31;
    bool changeMultiplier = false;
    int index = 1;

    //compare all public properties
    PropertyInfo[] publicProperties = this.GetType().GetProperties();

    if ((object)publicProperties != null
        &&
        publicProperties.Any())
    {
        foreach (var item in publicProperties)
        {
            object value = item.GetValue(this, null);

            if ((object)value != null)
            {

                hashCode = hashCode * ((changeMultiplier) ? 59 : 114) + value.GetHashCode();

                changeMultiplier = !changeMultiplier;
            }
            else
                hashCode = hashCode ^ (index * 13);//only for support {"a",null,null,"a"} <> {null,"a","a",null}
        }
    }

    return hashCode;
}

public static bool operator ==(ValueObject<TValueObject> x, ValueObject<TValueObject> y)
{
    // If both are null, or both are same instance, return true.
    if (System.Object.ReferenceEquals(x, y))
    {
        return true;
    }

    // If one is null, but not both, return false.
    if (((object)x == null) || ((object)y == null))
    {
        return false;
    }

    // Return true if the fields match:

    return x.Equals(y);

}

public static bool operator !=(ValueObject<TValueObject> x, ValueObject<TValueObject> y)
{
    return !(x == y);
}
}
Comments (3)

  1. fravelgue says:

    I like the term: Business Primitives (codebetter.com/…/business-primitives-1-2)

  2. felix says:

    Refresh your mind

    http://adf.ly/1isi5

  3. Keith J. Farmer says:

    I think you're risking an infinite loop in your Equals implementation.  Have you verified, for instance, the case where you're comparing a self-referential object? var a = new A();  var b = new A(); a.Value = b;  b.Value = a; var result = a.Equals(b);

    You could also shorten your equals implementation.  Consider publicProperties.All(p => p.GetValue(this, null).Equals(p.GetValue(other, null)).

    Similarly, you could use the Aggregate operator for your GetHashCode implementation.  Using LINQ operators could allow you to parallelize them.

Skip to main content