If you get tired of writing a lot of boilerplate for this, you can try using a base class that implements it for you.
public abstract class ValueObject<T> : IEquatable<T>
where T : ValueObject<T>
{
protected abstract IEnumerable<object> Reflect();
public override bool Equals(Object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (obj.GetType() != GetType()) return false;
return Equals(obj as T);
}
public bool Equals(T other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Reflect().SequenceEqual(other.Reflect());
}
public override int GetHashCode()
{
return Reflect().Aggregate(36, (hashCode, value) => value == null ?
hashCode : hashCode ^ value.GetHashCode());
}
public override string ToString()
{
return "{ " + Reflect().Aggregate((l, r) => l + ", " + r) + " }";
}
}
Now to make a value-like class, you just say:
public class Person : ValueObject<Person>
{
public int Age { get; set; }
public string Name { get; set; }
protected override IEnumerable<object> Reflect()
{
return new object[] { Age, Name };
}
}
In the Reflect
override you return a sequence of values that need to contribute to equality.
Unfortunately this approach can't help with declaring operator ==
as that has to be specifically declared on the derived type.