For the code below
public struct Person { public int ID; public static bool operator ==(Person a, Person b) { return a.Equals(b); } public static bool operator !=(Person a, Person b) { return !a.Equals(b); } }
Why does the compiler give me these warnings?
What's wrong with not defining the methods below?
warning CS0660: 'Person' defines operator == or operator != but does not override Object.Equals(object o) warning CS0661: 'Person' defines operator == or operator != but does not override Object.GetHashCode()
Why is it important to override GetHashCode ? It s important to implement both equals and gethashcode, due to collisions, in particular while using dictionaries. if two object returns same hashcode, they are inserted in the dictionary with chaining. While accessing the item equals method is used.
Your IEquatable<T>. Equals implementation should return results that are consistent with Equals. If your programming language supports operator overloading and you overload the equality operator for a given type, you must also override the Equals(Object) method to return the same result as the equality operator.
EDIT: This answer has been corrected, among other things to note that user-defined value types don't generate ==
, and to mention the performance issues with ValueType.Equals
.
In general, overridding one, but not all, is confusing. The user expects neither to be overridden, or both to be, with the same semantics.
Microsoft's recommendations for this state (among other things):
Implement the GetHashCode method whenever you implement the Equals method. This keeps Equals and GetHashCode synchronized.
Override the Equals method whenever you implement the equality operator (==), and make them do the same thing.
In your case, you have a valid reason to defer to Equals
(the compiler doesn't automatically implement ==
) and override only those two (==
/!=
). However, there's still a performance issue, since ValueType.Equals
uses reflection:
"Override the Equals method for a particular type to improve the performance of the method and more closely represent the concept of equality for the type."
Thus, it's still recommended to override all (==
/!=
/Equals
) in the end. Of course, performance may not matter for this trivial struct.
There is a general expectation within the Framework that certain operations should always produce the same result. The reason is that certain operations (in particular, sorting and searching, which make up a large portion of any application) rely on these different operations producing meaningful and consistent results. In this case, you are breaking a couple of those assumptions:
==
between a
and b
, it should produce the same result as a.Equals(b)
!=
between a
and b
, it should produce the same result as !a.Equals(b)
a
and b
exist, for which a == b
, then a
and b
should produce the same key when stored in a hash table.The first two, IMO, are obvious; if you are defining what it means for two objects to be equal, you should include all of the ways you can check for two objects to be equal. Note that the compiler doesn't (in general, cannot) enforce that you actually follow those rules. It's not going to perform complex code analysis of the body of your operators to see if they already mimic Equals
because, in the worst case, that could be equivalent to solving the halting problem.
What it can do, however, is check for cases where you most likely are breaking those rules, in particular, you provided custom comparison operators and did not provide a custom Equals
method. The assumption here is that you would not have bothered to provide operators if you did not want them to do something special, in which case, you should have provided custom behavior for all of the methods that need to be in sync.
If you did implement Equals
to be something different from ==
the compiler would not complain; you would have hit the limit of how hard C# is willing to try to prevent you from doing something stupid. It was willing to stop you from accidentally introducing subtle bugs in your code, but it's going to let you purposely do so if that's what you want.
The third assumption has to do with the fact that many internal operations in the Framework use some variant of a hash table. If I have two objects that are, by my definition, "equal", then I should be able to do this:
if (a == b) { var tbl = new HashTable(); tbl.Add(a, "Test"); var s = tbl[b]; Debug.Assert(s.Equals("Test")); }
This is a basic property of hash tables that would cause very strange problems if it were suddenly not true.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With