1

There is a passage from NHibernate documentation:

Note: if you define an ISet of composite elements, it is very important to implement Equals() and GetHashCode() correctly.

What does correctly mean there? Is it neccessary to implement those methods for all value objects in domain?

EXTENDING MY QUESTION

In the article Marc attached user Albic states:

It's actually very hard to implement GetHashCode() correctly because, in addition to the rules Marc already mentioned, the hash code should not change during the lifetime of an object. Therefore the fields which are used to calculate the hash code must be immutable.

I finally found a solution to this problem when I was working with NHibernate. My approach is to calculate the hash code from the ID of the object. The ID can only be set though the constructor so if you want to change the ID, which is very unlikely, you have to create a new object which has a new ID and therefore a new hash code. This approach works best with GUIDs because you can provide a parameterless constructor which randomly generates an ID.

I suddenly realized what I've got inside my AbstractEntity class:

public abstract class AbstractEntity<T> where T : AbstractEntity<T> {
    private Nullable<Int32> hashCode;
    
    public virtual Guid Id { get; protected set; }
    public virtual Byte[] Version { get; set; }

    public override Boolean Equals(Object obj) {
        var other = obj as T;
        if(other == null) {
            return false;
        }

        var thisIsNew = Equals(this.Id, Guid.Empty);
        var otherIsNew = Equals(other.Id, Guid.Empty);

        if(thisIsNew && otherIsNew) {
            return ReferenceEquals(this, other);
        }

        return this.Id.Equals(other.Id);
    } // public override Boolean Equals(Object obj) {

    public override Int32 GetHashCode() {
        if(this.hashCode.HasValue) {
            return this.hashCode.Value;
        }

        var thisIsNew = Equals(this.Id, Guid.Empty);
        if(thisIsNew) {
            this.hashCode = base.GetHashCode();
            return this.hashCode.Value;
        }
        return this.Id.GetHashCode();
    } // public override Int32 GetHashCode() {
    
    public static Boolean operator ==(AbstractEntity<T> l, AbstractEntity<T> r) {
        return Equals(l, r);
    }
    public static Boolean operator !=(AbstractEntity<T> l, AbstractEntity<T> r) {
        return !Equals(l, r);
    }
} // public abstract class AbstractEntity<T>...

As all components are nested within entities should I then implement Equals() and GetHashCode() for them?

Community
  • 1
  • 1
lexeme
  • 2,915
  • 10
  • 60
  • 125
  • 1
    Related : http://stackoverflow.com/questions/371328/why-is-it-important-to-override-gethashcode-when-equals-method-is-overridden?rq=1 – Marc Gravell Apr 11 '13 at 11:17

2 Answers2

1

Correctly means that GetHashCode returns the same hash code for the entities that are expected to be equal. Because equality of 2 entities is made by comparison of that code.

On the other side, that means that for entities that are not equal, the uniqueness of hash code has to be guaranteed, as much as it possible.

Tigran
  • 61,654
  • 8
  • 86
  • 123
  • should this be applied only to `components` in sets or to all `components` (`value objects`) in domain? – lexeme Apr 11 '13 at 11:05
  • 1
    Given that GetHashCode generally represents a loss of fidelity (i.e. more data is encoded in the object than in the resultant 32-bit hash), you cannot guarantee uniqueness of a hashcode for unequal objects. Ergo, your last point is incorrect. Collisions in hashes are inevitable (unless you're hashing something carrying less than or equal 32bits of info and you cook up a perfect hash) – spender Apr 11 '13 at 11:08
  • it depends on the *logic* of your app. So the logic is: make gethashcode return the same value for all components that in *your* system has to be recoginzed like equals. Example: if I look on specialists, doctor and driver are not equal, if I look from biology perspective they *are* equal(both humans). So depends on how *yuor application* should work. – Tigran Apr 11 '13 at 11:09
  • @spender: there is no gurantee for anything in the world, my comment, hopefully, explains a target OP should looking on, to find a best decision. By the way, thanks, would refine an answer. – Tigran Apr 11 '13 at 11:10
1

The documentation for Equals and GetHashCode explain this well and include specific guidance on implementation for value objects. For value objects, Equals is true if the objects are the same type and the public and private fields are equal. However, this explanation applies to framework value types and you are free to create your own Equals by overriding it.

GetHashCode has two rules that must be followed:

  • If two objects compare as equal, the GetHashCode method for each object must return the same value. However, if two objects do not compare as equal, the GetHashCode methods for the two object do not have to return different values.

  • The GetHashCode method for an object must consistently return the same hash code as long as there is no modification to the object state that determines the return value of the object's Equals method. Note that this is true only for the current execution of an application, and that a different hash code can be returned if the application is run again.

Jamie Ide
  • 48,427
  • 16
  • 81
  • 117