lexeme
lexeme

Reputation: 2973

Implemeting GetHashCode and Equals methods for ValueObjects

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?

Upvotes: 1

Views: 316

Answers (2)

Jamie Ide
Jamie Ide

Reputation: 49261

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.

Upvotes: 1

Tigran
Tigran

Reputation: 62266

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.

Upvotes: 1

Related Questions