A domain class T
can be of type ValueObject<T>
:
public class Coordinate: ValueObject<Coordinate>
{ ... }
ValueObject<T>
implements the IEquatable
interface. I want each concrete implementation of ValueObject<T>
to provide implementation for bool Equals(T obj)
, so I have created it as an abstract method:
public abstract class ValueObject<T> : IEquatable<T>
{
public abstract bool Equals(T obj);
public static bool operator ==(ValueObject<T> obj1, ValueObject<T> obj2)
{
if (object.ReferenceEquals(obj1, obj2)) return true;
if (object.ReferenceEquals(obj1, null)) return false;
if (object.ReferenceEquals(obj2, null)) return false;
return obj1.Equals(obj2);
}
}
An Equals implementation in the Coordinate
class:
public class Coordinate : ValueObject<Coordinate>
{
// ...
public override bool Equals(Coordinate other)
{
return (other != null) && (this.Latitude == other.Latitude) && (this.Longitude == other.Longitude);
}
}
ValueObject<T>
provides generic operating overriding for == (and for !=, which is not shown above), which applies to all concrete implementations.
The problem is that when the Equals
method is called from either the == override, it calls Object.Equals()
and not Coordinate.Equals()
.
The problem is that when the
Equals
method is called from either the==
override, it callsObject.Equals()
and notCoordinate.Equals()
.
No, the problem is that those things are different. If they are the same, as they should be, then there's no problem.
So make them the same. Don't make the derived class do the wrong thing; force them to do the right thing.
public abstract class ValueObject<T> : IEquatable<T>
{
// Force the derived class to override these.
public abstract override bool Equals(object obj);
public abstract override int GetHashcode(object obj);
// And then consistently use the overridden method as the implementation.
public virtual bool Equals(T obj)
{
return obj1.Equals((object)obj2);
}
public static bool operator ==(ValueObject<T> obj1, ValueObject<T> obj2)
{
return obj1.Equals((object)obj2);
}
public static bool operator !=(ValueObject<T> obj1, ValueObject<T> obj2)
{
return !obj1.Equals((object)obj2);
}
}