How does one override .Equals() for a Discriminated Union?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP



How does one override .Equals() for a Discriminated Union?



I have a discriminated union type and want to override .Equals().


.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.


int



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.


Name



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.


equals


List


List.contains



Any suggestions?




1 Answer
1



The error is telling you that, since you're overriding the Equals method, it's a very good idea to override GetHashCode as well.


Equals


GetHashCode



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.


GetHashCode


Equals


GetHashCode



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.


GetHashCode


Equals


GetHashCode



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:


Equals


Name


GetHashCode


[<CustomEquality>]
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:


Equals


null


override this.Equals (o: obj) =
match o with
| :? ModelArg as ma -> this.Name = ma.Name
| _ -> false





Thanks. I tried this but got a message: "The CustomEquality attribute must be used in conjunction with the NoComparison or CustomComparison attributes. I added the former and everything worked fine. I also modified your suggestion for handling the case in which the argument is of another type: I generate an exception in that case.
– Soldalma
Aug 6 at 3:10


CustomEquality


NoComparison


CustomComparison






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Popular posts from this blog

Firebase Auth - with Email and Password - Check user already registered

Dynamically update html content plain JS

How to determine optimal route across keyboard