Is this a good implementation for Equals and GetHashCode for a base class in C#? If it's not good enough, can you suggest improvements for it, please?
public abstract class Entity<TKey> //TKey = Type of the Key
{
private string FullClassName;
private bool KeyIsNullable;
private Type BaseClassType;
private bool KeyIsComplex;
public abstract TKey Key { get; } //Key of the object, which determine it's uniqueness
public Entity()
{
FullClassName = GetType().FullName + "#";
KeyIsNullable = typeof(TKey).IsAssignableFrom(typeof(Nullable));
BaseClassType = typeof(Entity<TKey>);
KeyIsComplex= !typeof(TKey).IsPrimitive;
}
public override bool Equals(object obj)
{
bool result = BaseClassType.IsAssignableFrom(obj.GetType());
result
= result
&& (
(
(
!KeyIsNullable
||
(Key != null && ((Entity<TKey>)obj).Key != null
)
)
&&
Key.Equals(((Entity<TKey>)obj).Key )
) // The key is not nullable, or (it's nullable but) both aren't null, and also equal
||
(
KeyIsNullable
&&
Key == null
&&
((Entity<TKey>)obj).Key == null
)
); // Or the key is nullable, and both are null
return result;
}
public override int GetHashCode()
{
if ((KeyIsNullable&& Key == null) || (!KeyIsNullable&& Key .Equals(default(TKey))))
{
return base.GetHashCode();
}
string stringRepresentation = FullClassName + ((KeyIsComplex)? Key.GetHashCode().ToString() : Key.ToString());
return stringRepresentation.GetHashCode();
}
}
Example of a derived class:
public class Foo : Entity<int>
{
public virtual int FooId { set; get; }
public virtual string FooDescription { set; get; }
public override int Key { get { return FooId; } }
}
Specific and special details to the proposal:
- Any instance of a derived class is considered equal to a instance of the base class if they have the same key.
- The key could be
null. - If the key of the current class and the key of the comparing object are both
null, the two objects are considered equal. This is because I am planning to handle just one new object at a time, and if the key is nullable, it will benullfor the new object. So if I have two instances with anullkey, I will consider them as the same entity.
IEqualityComparer<T>that compares byId, but I'd avoid making it the default. \$\endgroup\$new Foo { FooId = 42 }.Equals(new Bar { BarId = 42 })should returntrue? That would be very weird. \$\endgroup\$