0

Visual Studio is offering to generate code for Equals() and GetHashCode() two ways.

public class Identifier
{
    public string firstName;
    public string lastName;
    internal Identifier(string firstName, string lastName) { this.firstName = firstName; this.lastName = lastName; }
    public override bool Equals(object obj)
    {
        return obj is Identifier identifier &&
               firstName == identifier.firstName &&
               lastName == identifier.lastName;
    }
    public override int GetHashCode()
    {
        return HashCode.Combine(firstName, lastName);
    }
    public override string ToString() { return $"{this.firstName} {this.lastName}"; }
}

The other way...

public class Identifier : IEquatable<Identifier>
{
    public string firstName;
    public string lastName;
    internal Identifier(string firstName, string lastName) { this.firstName = firstName; this.lastName = lastName; }
    public override bool Equals(object obj)
    {
        return Equals(obj as Identifier);
    }
    public bool Equals(Identifier other)
    {
        return other != null &&
               firstName == other.firstName &&
               lastName == other.lastName;
    }
    public override int GetHashCode()
    {
        return HashCode.Combine(firstName, lastName);
    }
    public override string ToString() { return $"{this.firstName} {this.lastName}"; }
}

What are the practical differences that would guide the choice? Does the first case exist simply because some people prefer less verbose solutions?

Edit I just wanted to add some thoughts on reducing clutter. If the code has adequate testing the success of tests can be used to "prove" that GetHashCode is not necessary (for example because the type is not used as a Dictionary key) in which case GetHashCode can be omitted and clutter reduced. IEquatable<T> does not require GetHashCode. Note that in both cases in the above, override bool Equals(object obj) is code generated but it might be a good idea to leave it out if you know what you're doing because it's clutter or you might want to override bool Equals(object obj) { Debug.Assert(false); return false; } to force an "early" failure and have the consumer use bool Equals(T obj) where T is the appropriate type. Unfortunately if you override bool Equals(object obj) then you get a warning about the absence of GetHashCode and using metadata to hide warnings is annoying creates clutter or busy work setting compiler options.

Edit I settled on override boo Equals(object obj) and override int GetHashCode without implementing IEquatable<T> because 2 methods is less clutter than 3, and there will be no worry about Dictionary lookup bugs, and I don't need : IEquatable<T> and bool Equals(T obj), the absence of which Dictionary and List do not trigger complaints. This solution involves casting but premature optimization is bad.

H2ONaCl
  • 10,644
  • 14
  • 70
  • 114
  • 2
    [Does this answer your question?](https://stackoverflow.com/a/2477000/3181933) – ProgrammingLlama Mar 01 '21 at 07:58
  • The main difference is really simply if you prefer an own function for the specific type. But IMHO you should NEVER implement them. Instead implement and own class with interface `IEqualityComparer` or `IComparer`. Otherwise you could run into strange behaviour, especially with derived classes. – Oliver Mar 01 '21 at 08:04
  • 1
    @John, Thanks. Boxing and reflecting a struct sounds slow. Casting is trivial. – H2ONaCl Mar 01 '21 at 08:22
  • @Oliver do you know of a specific bug that could help me to understand why you never implement Equals and GetHashCode ? – H2ONaCl Mar 01 '21 at 09:20
  • I would guess that `Equals(object)` is partially due to historical reasons. .Net v1 did not have generics, but still wanted to support comparing arbitrary types, for example as dictionary keys. – JonasH Mar 01 '21 at 10:01

0 Answers0