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);}}