I just wanted to share my attempt for implementing a generic base class for Value Objects, popularized by Eric Evans and the Domain-Driven Design community. I must say that I got heavily inspired by Jimmy Bogard's implementation, which got me thinking about such an approach. Contrary to his implementation, I used static reflection instead of dynamic reflection in order to determine which fields to use for equality and string representation.
public abstract class ValueObject<T> : IEquatable<T>
where T : ValueObject<T>
{
private List<PropertyInfo> Properties { get; set; }
protected ValueObject()
{
Properties = new List<PropertyInfo>();
}
public override Boolean Equals(Object obj)
{
if(ReferenceEquals(null, obj)) return false;
if(obj.GetType() != GetType()) return false;
return Equals(obj as T);
}
public Boolean Equals(T other)
{
if(ReferenceEquals(null, other)) return false;
if(ReferenceEquals(this, other)) return true;
foreach(var property in Properties)
{
var oneValue = property.GetValue(this, null);
var otherValue = property.GetValue(other, null);
if(null == oneValue && null == otherValue) return false;
if(false == oneValue.Equals(otherValue)) return false;
}
return true;
}
public override Int32 GetHashCode()
{
var hashCode = 36;
foreach(var property in Properties)
{
var propertyValue = property.GetValue(this, null);
if(null == propertyValue)
continue;
hashCode = hashCode ^ propertyValue.GetHashCode();
}
return hashCode;
}
public override String ToString()
{
var stringBuilder = new StringBuilder();
foreach(var property in Properties)
{
var propertyValue = property.GetValue(this, null);
if(null == propertyValue)
continue;
stringBuilder.Append(propertyValue.ToString());
}
return stringBuilder.ToString();
}
protected void RegisterProperty(
Expression<Func<T, Object>> expression)
{
Check.Argument(expression, "expression").IsNotNull();
MemberExpression memberExpression;
if(ExpressionType.Convert == expression.Body.NodeType)
{
var body = (UnaryExpression)expression.Body;
memberExpression = body.Operand as MemberExpression;
}
else
{
memberExpression = expression.Body as MemberExpression;
}
if(null == memberExpression)
{
var message = ResourceLoader<ValueObject<T>>
.GetString("InvalidMemberExpression");
throw new InvalidOperationException(message);
}
Properties.Add(memberExpression.Member as PropertyInfo);
}
}
This generic base class takes care of the equality by overriding Equals and GetHashCode from the Object class and implementing the IEquality interface. It also takes care of a default implementation of the ToString method.
Using this base class significantly reduces the amount of code for implementing a value object in the domain.
public class Tag : ValueObject<Tag>
{
public String Name { get; private set; }
public Tag(String name)
{
Name = name;
RegisterProperty(value => value.Name);
}
}
And that is that. The only thing I've omitted in this example is the validation of the specified name.
I've been using this base class in a couple of projects now, and so far, I've been very pleased with the results although it can always be improved. I'd love to read your comments.