Writing A Good C# Equals Method
Asked Answered
I

8

18

Does anyone have a template for writing a decent equals method - I remember in Effective Java there was problems around handling equals when dealing with subclasses.

I dont have the book with me and I cannot remember if it was practical advice - So how do you write a solid robust equals method implementation?

Ingleside answered 24/3, 2010 at 17:31 Comment(0)
B
12

Possibly an off-the-wall suggestion but: consider not overriding Equals in the first place. Basically the nature of equality doesn't work well with subclassing, as you mentioned. However, almost everywhere in the .NET API which uses equality (e.g. dictionaries, hash sets) allows an IEqualityComparer<T> to be passed in. Making a different object responsible for equality makes life much more flexible: you can use different objects to determine which criteria to use.

Implementing IEqualityComparer<T> is much simpler - you still need to check for nullity, but you don't need to worry about whether the types are appropriate, or whether Equals will be further overridden.

Another approach to making the normal Equals work more smoothly is to avoid inheritance entirely for the most part - I can't remember the last time it really made sense in my code to override Equals and allow derived classes. sealed FTW :)

Barite answered 24/3, 2010 at 17:43 Comment(0)
B
8

You may have already done this, but did you check out the MSDN Article on implementing Equals()?

How to define value equality for a class or struct

Implementing the Equals Method (old MSDN article)

Bernini answered 24/3, 2010 at 17:34 Comment(0)
S
6

Properties of a good equals method:

  • Symmetry: For two references, a and b, a.equals(b) if and only if b.equals(a)
  • Reflexivity: For all non-null references, a.equals(a)
  • Transitivity: If a.equals(b) and b.equals(c), then a.equals(c)
Shayla answered 24/3, 2010 at 17:42 Comment(0)
V
6

If you get tired of writing a lot of boilerplate for this, you can try using a base class that implements it for you.

public abstract class ValueObject<T> : IEquatable<T>
    where T : ValueObject<T>
{
    protected abstract IEnumerable<object> Reflect();

    public override bool Equals(Object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (obj.GetType() != GetType()) return false;
        return Equals(obj as T);
    }

    public bool Equals(T other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Reflect().SequenceEqual(other.Reflect());
    }

    public override int GetHashCode()
    {
        return Reflect().Aggregate(36, (hashCode, value) => value == null ?
                                hashCode : hashCode ^ value.GetHashCode());
    }

    public override string ToString()
    {
        return "{ " + Reflect().Aggregate((l, r) => l + ", " + r) + " }";
    }
}

Now to make a value-like class, you just say:

public class Person : ValueObject<Person>
{
    public int Age { get; set; }
    public string Name { get; set; }

    protected override IEnumerable<object> Reflect()
    {
        return new object[] { Age, Name };
    }
}

In the Reflect override you return a sequence of values that need to contribute to equality.

Unfortunately this approach can't help with declaring operator == as that has to be specifically declared on the derived type.

Vantage answered 24/3, 2010 at 17:43 Comment(0)
H
2

Look at Guidelines for Overloading Equals() and Operator == on MSDN.

Hermit answered 24/3, 2010 at 17:40 Comment(0)
V
1

I generally do something like this:

public struct EmailAddress : IEquatable<EmailAddress>
{
    public override bool Equals(object obj)
    {
        return obj != null && obj.GetType() == typeof(EmailAddress) && Equals((EmailAddress)obj);
    }

    public bool Equals(EmailAddress other)
    {
        return this.LocalPart == other.LocalPart && this.Domain == other.Domain;
    }
}
Vesper answered 24/3, 2010 at 17:34 Comment(0)
E
0

A "good" equals method is a method that compares a unique part of a class while leaving out parts that do not contribute to the uniqueness. So, if you have a class with a id which is unique you can just use it to establish equality, leaving out any other properties. Often you will also need some timestamp value too, though.

Endways answered 24/3, 2010 at 17:36 Comment(0)
D
-1

public class Person { public string Name { get; set; }

    public int Age { get; set; }

    public override bool Equals(object obj)
    {
        var objAsPerson = obj as Person;

        if (obj == null)
        {
            return false;
        }

        if (this.Name != objAsPerson.Name)
        {
            return false;
        }

        if (this.Age != objAsPerson.Age)
        {
            return false;
        }

        return true;
    }
}
Ditty answered 8/12, 2015 at 13:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.