Metoda Equals() slouží v .NET Frameworku k ověření rovnosti. Její výchozí implementace Object.Equals() ověřuje referenční rovnost, tedy zda obě reference odkazují na stejnou instanci. U typů, které nesou nějaké smysluplné hodnoty, může být však vhodnější implementovat Equals jako ověření hodnotové rovnosti, byť by se jednalo o dvě různé instance.
.NET má metodu Object.Equals ve dvou podobách
- public virtual bool Equals(object obj) – k té chceme implementovat override
- public static bool Equals(object objA, object objB)
public static bool Equals(object objA, object objB) { if (objA == objB) { return true; } if ((objA != null) && (objB != null)) { return objA.Equals(objB); } return false; }
Statická metoda tedy nejprve ověří rovnost referencí, pokud jde o jednu instanci, vrátí true. Potom ověří, zda-li není jeden z objektů null. Pokud ano, vrátí false, pokud ne zavolá instanční podobu, kterou právě overridujeme.
Kontrakt
Nejprve základní pravidla, které musí Equals splňovat:
- reflexe – A.Equals(A) = true,
- symetrie – když A.Equals(B), pak B.Equals(A),
- tranzitivita – když A.Equals(B) a B.Equals(C), pak i A.Equals(C),
- konzistence – pokud nedojde ke změně objektů, pak A.Equals(B) musí dávat stále stejnou hodnotu,
- A.Equals(null) = false,
- metoda nesmí vyhazovat žádnou výjimku,
Doporučení:
- potomek by měl volat base.Equals(), pokud je to možné,
- měly bychom overridovat též GetHashCode(),
- pokud implementujeme IComparable, měli bychom overridovat Equals(),
- měly bychom vytvořit i strong-typed overload Equals().
Operátory ==, !=
Zároveň s override metody Equals() je vhodné přetížit i operátory == a !=, jejichž základní implementace ověřuje opět referenční shodu. Implementace je jednoduchá, prostě se odkážeme opět na statické Object.Equals()
Implementace
Ukažme si tedy běžnou implementaci pro business-object, u kterého nám stačí shoda ID.
public virtual bool Equals(BusinessObjectBase obj) { if ((obj == null) || (this.GetType() != obj.GetType())) { return false; } if (!Object.Equals(this.ID, obj.ID)) { return false; } return true; } public override bool Equals(object obj) { if (obj is BusinessObjectBase) { BusinessObjectBase bob = obj as BusinessObjectBase; return this.Equals(bob); } return false; } public static bool operator ==(BusinessObjectBase objA, BusinessObjectBase objB) { return Object.Equals(objA, objB); } public static bool operator !=(BusinessObjectBase objA, BusinessObjectBase objB) { return !Object.Equals(objA, objB); } public override int GetHashCode() { return this.ID; }
Samotné porovnávání členských hodnot, které determinují shodu, je opět vhodné nechat na statické metodě Object.Equals().
Ještě si ukažme, jak by měla vypadat implementace potomka, který by chtěl mimo ID hlídat ještě shodu na nějakou další hodnotu (doporučené volání base.Equals())
public override Equals(Order order) { return base.Equals((BusinessObjectBase)order) && Object.Equals(this.Cena, order.Cena); } public override bool Equals(object obj) { if (obj is Order) { Order order = obj as Order; return this.Equals(order); } return false; }