✅ 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
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
Not all things that do override equals are conceptually records.
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.
things are should have value semantics are by definition conceptually records
unless youre doing a weird implementation of Equals
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?
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
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.
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
wait why? note I assume GetHashCode and Equals are being overridden.
what objects in the bcl do that?
and why would they
System.Reflection.Assembly doesn't implement IEquatable<T>. but it has operators.
AuthenticationHeaderValue Class (System.Net.Http.Headers)
Represents authentication information in Authorization, ProxyAuthorization, WWW-Authenticate, and Proxy-Authenticate header values.
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
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
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?
What's your question?
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.
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.
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
whats YAGNI?
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.
but technically you never need it
I have no opinions on general practice. Do what you need
That's definitely not true. If you use the interface, then implement it
if youre overriding Equals then you intend to use it, meaning youd expect to implement IEquatable
if you're writing a library, you're not necessarily using the interface, you're imagining how others can do.
Not necessarily, no. Implement it if you use it
You're pressing me for an opinion I do not have
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