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.
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 abstract class ValueObject : IEquatable
where T : ValueObject
{
private List Properties { get; set; }
protected ValueObject()
{
Properties = new List();
}
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>
.GetString("InvalidMemberExpression");
throw new InvalidOperationException(message);
}
Properties.Add(memberExpression.Member as PropertyInfo);
}
}</valueobject</func<t,></propertyinfo></propertyinfo> </t></span></code></pre>
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.
</div>