C
C#5w ago
webczat

✅ Equality confusion

My question is about equality on reference types. Most ms docs state that full equality contract should be implemented only for objects having value semantics, although examples usually talk about things like complex numbers. Records are just glorified classes with multiple properties and have that implemented too. By full equality contract I mean object.Equals + GetHashCode + IEquatable + operators. Docs on IEquatable<T> say that if you implement it you should also implement operators. Docs for object.Equals say that most reference types shouldn't override operators even if they implement equals. Why would a type implement Equals but not operators and not IEquatable<T>? Ah, and there is even an analyzer warning about implementing operators.
27 Replies
sibber
sibber5w ago
Docs for object.Equals say that most reference types shouldn't override operators even if they implement equals.
where does it say this? i cant find that it also doesnt make sense if you override/implement one of the equality checkers, you should override/implement all otherwise you get weird behavior thats prone to bugs bevause no one would expect it its also what records do, for the same reason so in short, you should do what records do actually, you should just use records
webczat
webczatOP5w ago
Not all things that do override equals are conceptually records.
webczat
webczatOP5w ago
see: https://learn.microsoft.com/en-us/dotnet/fundamentals/runtime-libraries/system-object-equals#guidelines-for-reference-types exactly:
Most reference types must not overload the equality operator, even if they override Equals. However, if you are implementing a reference type that is intended to have value semantics, such as a complex number type, you must override the equality operator.
System.Object.Equals method - .NET
Learn about the System.Object.Equals method.
sibber
sibber5w ago
things are should have value semantics are by definition conceptually records unless youre doing a weird implementation of Equals
webczat
webczatOP5w ago
I am not sure if all of these types need value semantics. and how do you define value semantics. for example if i have some User object where Equals just compares user ids disregarding all other fields, is it a value or not?
sibber
sibber5w ago
ah well thats weird if youre overriding Equals but not for value semantics then id just add a custom method for it using Equals for that seems wrong and i also cant think of anything that does this
webczat
webczatOP5w ago
depends. for example in this specific case like comparing just ids, somehow it looks even worse to me. I've seen things in bcl that override object.Equals but not implement IEquatable. as for types that override everything but == and != ops, unsure.
sibber
sibber5w ago
another weird side effect you get is that Equals might somtimes retyrn true when 2 objects have different hashes which i guess wouldnt break anything, since the data structures that depend on hashing only use Equals as a fallback but its still weird
webczat
webczatOP5w ago
wait why? note I assume GetHashCode and Equals are being overridden.
sibber
sibber5w ago
what objects in the bcl do that? and why would they
webczat
webczatOP5w ago
System.Reflection.Assembly doesn't implement IEquatable<T>. but it has operators.
webczat
webczatOP5w ago
AuthenticationHeaderValue Class (System.Net.Http.Headers)
Represents authentication information in Authorization, ProxyAuthorization, WWW-Authenticate, and Proxy-Authenticate header values.
webczat
webczatOP5w ago
this implements object.Equals without implementing anything else. I assume GHC too, but let's skip it and yes, I actually needed to compare authentication headers in my code and for me it is conceptually a value to compare and it should have full contract. might be reason is compat, but because it would be source breaking change
sibber
sibber5w ago
huh its overriding it but keeping the default behavior wtf am i missong something? weird i never wouldve guessed == to have different behavioe i dont like tgis
webczat
webczatOP5w ago
that's why I started asking questions. I am designing types that will need and require full contracts, as they aren't records but are small structs. But I know I will then likely have some types that I might have this question for in the later dev phases. as in immutable "entities" but not in the meaning of a db entity, more like a http api returned entity identified by id, but whose identity if any is defined purely by the id so whether I would implement any equality for them is to be seen, but it's possible. they're definitely not to be compared by fields. they're going to be immutable. internally holding immutable records coming from json this is just an example, I'm not at that level yet. however, I know this question will bite me back I hereby summon @333fred. Any insights from the perspective of a c# designer?
333fred
333fred5w ago
What's your question?
webczat
webczatOP5w ago
probably it can be shortened to: when if at all it makes sense (for reference types) to implement object.Equals but not the rest of equality contract... Yes, by object.Equals I mean GetHashCode too, but not others. Or why would such an object not implement IEquatable<T>... etc Question invoked by this thing in object.Equals remark:
Most reference types must not overload the equality operator, even if they override Equals. However, if you are implementing a reference type that is intended to have value semantics, such as a complex number type, you must override the equality operator.
webczat
webczatOP5w ago
and by things like https://learn.microsoft.com/en-us/dotnet/api/system.net.http.headers.authenticationheadervalue?view=net-9.0 that in my view should kinda implement full contract but don't. and are actually new'ish
AuthenticationHeaderValue Class (System.Net.Http.Headers)
Represents authentication information in Authorization, ProxyAuthorization, WWW-Authenticate, and Proxy-Authenticate header values.
333fred
333fred5w ago
Because the owner didn't want to? I have no perspective here YAGNI applies everywhere. If you aren't going to need the interface, why bother adding it
sibber
sibber5w ago
whats YAGNI?
webczat
webczatOP5w ago
you are not gonna need it i specifically ask about practice in general, not necessarily about these specific cases as in it's less important why == and != was not done for AuthenticationHeaderValue specifically, but unsure when I myself would or wouldn't.
sibber
sibber5w ago
but technically you never need it
333fred
333fred5w ago
I have no opinions on general practice. Do what you need That's definitely not true. If you use the interface, then implement it
sibber
sibber5w ago
if youre overriding Equals then you intend to use it, meaning youd expect to implement IEquatable
webczat
webczatOP5w ago
if you're writing a library, you're not necessarily using the interface, you're imagining how others can do.
333fred
333fred5w ago
Not necessarily, no. Implement it if you use it You're pressing me for an opinion I do not have
webczat
webczatOP5w ago
this is the problem. I also don't and trying to come up with something, but I have no data and became confused I am imagining examples of things I will likely implement in current project and can't get a definitive answer at this point except some structs where answer is obvious including one where I really chose not to implement any equality and I know why. as in on a struct wrapper

Did you find this page helpful?