Alapszabály, hogy HashSet, Dictionary vagy bármi más asszociatív kollekciókban kulcsként csak immutable objektumokat lehet használni. Ennek az az oka, hogy amikor berakunk egy elemet, akkor a kollekció meghívja az objektumon a GetHashCode-ot, és az azonos hashkódú objektumokat listába szervezi. Ha két objektum egyenlőnek tekintendő (az Equals alapján), akkor a hascodejuknak azonosnak kell lenni. Ha ez nem teljesül, vagy idővariáns, azaz később már nem igaz, akkor ezek a kollekciók meghülyülnek.
A példám így nézett ki:
public virtual bool Equals(EntityBase other)
{
if (other == null)
{
return false;
}
if (Id == 0)
{
//Transient object
return ReferenceEquals(this, other);
}
return other.Id == Id;
}
public override bool Equals(object obj)
{
if (!(obj is EntityBase))
{
return false;
}
return Equals((EntityBase)obj);
}
public override int GetHashCode()
{
return Id;
}
Eleve látszik, a GetHashCode és az Equals nem azonos logika szerint működik. A GetHashCode akkor ad vissza azonos hascodeot, ha az idk azonosak. Az Equals viszont az idtől függően vagy az idkat hasonlítja össze, vagy referenciális komparálást csinál. Ez már kapásból bug.
A másik bug, hogy ez az objektum nem immutable, az Id változik, amikor adatbázisba beszúrjuk az entitást, és kap egy idt. Emiatt az egész koncepció beteg.
A HashSet.Add meghívja a HashSet.AddIfNotPresetet, amiben van egy ciklus:
for (int i = this.m_buckets[num % this.m_buckets.Length] - 1; i >= 0; i = this.m_slots[i].next)
{
if (this.m_slots[i].hashCode == num && this.m_comparer.Equals(this.m_slots[i].value, value))
{
return false;
}
num3++;
}
Ez az ciklus akadt be. Az if feltétele sose teljesült, így sose lépett kis a ciklusból, mivel idt kapott az entitás, így megváltozott az Equals logikája.