I have a discriminated union type and want to override .Equals()
.
In this simple example I could have used the .Equals function for int
to solve the problem, but in my code otherStuff does not support structural comparison.
The following code was my best try:
[<CustomEquality>]
type ModelArg = { Name: string; OtherStuff: int}
with override this.Equals (o: obj) = this.Name = (o :?> ModelArg).Name
I then got a red squiggly line and the following message:
"The struct, record or union type 'ModelArg' has an explicit implementation of 'ObjectEquals'. Consider implementing a matching override for 'Object.GetHashCode'."
I would like to avoid doing that because I really only care for the field Name
and also, for performance reasons.
Of course I could write an equals
function but I would not be able to use it with List
functions like List.contains
and I need to do that.
Any suggestions?
The error is telling you that, since you're overriding the Equals
method, it's a very good idea to override GetHashCode
as well.
The reason for this is that in .NET in general (not just in F#), hash codes are often used as an approximation of equality. For example, if you were to put your objects in a hash table, the hash table would distribute them between buckets based on GetHashCode
, and would look them up in the buckets that way too. Then, if Equals
is implemented differently than GetHashCode
, the hash table's behavior will be unpredictable - it might fail to look up an object that was just inserted or something similar.
Further, the error message does not suggest that you include the int in the definition of equality. All it says is that you need to implement GetHashCode
, and do it in the same sense as your Equals
implementation. There is also no performance penalty for doing this, as long as you never actually call GetHashCode
. And if you do - see above.
Since all your Equals
implementation does is compare the Name
field, it would probably make sense to delegate GetHashCode
to the same field as well:
[<CustomEquality; NoComparison>]
type ModelArg = { Name: string; OtherStuff: int}
with
override this.Equals (o: obj) = this.Name = (o :?> ModelArg).Name
override this.GetHashCode() = this.Name.GetHashCode()
Finally, your implementation of Equals
would crash when called with a null
or with an object of another type. I would suggest that you handle this case if you want your code to be robust:
override this.Equals (o: obj) =
match o with
| :? ModelArg as ma -> this.Name = ma.Name
| _ -> false