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.