Equals method and immutable objects

E

Edward Diener

My understanding is that the Equals method is to be used only on immutable
objects. In that case is there any CLS compliant method my GC or value
classes should use to determine whether one mutable object of a given type
is equal to another mutable object of the same type as far as the data in
the objects are concerned, or is the name of my method purely of my own
invention ?
 
J

Jon Skeet [C# MVP]

Edward Diener said:
My understanding is that the Equals method is to be used only on immutable
objects.

I don't think so. Only immutable objects are likely to be useful in a Hashtable
or similar structure (which is one of the times that Equals needs to be
overridden) but I don't see anything wrong with overriding it for
mutable objects too.

StringBuilder overrides Equals, for instance.
 
E

Edward Diener

Jon said:
I don't think so. Only immutable objects are likely to be useful in a
Hashtable or similar structure (which is one of the times that Equals
needs to be overridden) but I don't see anything wrong with
overriding it for
mutable objects too.

StringBuilder overrides Equals, for instance.

My understanding is that if two objects return true to Equals, they must
have the same GetHashCode value, and that GetHashCode must not change for a
given object. I therefore don't see how this can ever be implemented unless
the objects are themselsves immutable. If I base my GetHashCode on field
values in the object, I must use those same values for the Equals method,
else I can not guarantee that two objects which return true from Equals also
have the same GetHashCode. Since those values can not change, or else
GetHashCode changes, my object is immutable as far as I am concerned. As an
example in C#:

class X
{
int val1 = 2;
int val2 = 3;
// Other fields
public int GetHashCode() { return ( val1 ^ val2 ); }
public static bool Equals ( X first, X second ) { return(first.val1 ==
second.val1 && first.val2 == second.val2); }
}

If val1 or val2 can ever change, GetHashCode is broken as it will return a
different value. However, Equals should return different results if val1 or
val2 changes. Yet this can not happen, so my conclusion is that the object
must be immutable. If I base my Equals on other fields, I can never
guarantee that GetHashCode and Equals are synced to each other. Clearly an
Equals method for an object should be based on all significant fields in the
object, not just immutable fields. But if this is the case, then all
significant fields in the object must be immutable because if they are not,
GethashCode and Equals will never be in sync with each other. Have I missed
something in my reasoning ? To me, Equals should work on mutable objects,
ie. objects whose field values change, yet this is impossible as long as the
rule exists that GetHashCode and Equals sync with each other.
 
J

Jon Skeet [C# MVP]

Edward Diener said:
My understanding is that if two objects return true to Equals, they must
have the same GetHashCode value

Certainly if you want them to work in a Hashtable.
and that GetHashCode must not change for a
given object.

Hmm... interestingly, that *is* what the documentation says for
GetHashcode, but it doesn't make much sense to me.

Interestingly, StringBuilder seems to conform to this part, but not the
"equal objects should return the same hash code" part.

I would suggest sticking to the first rule but not the second. Then if
your objects are used in a Hashtable they'll be fine so long as they
don't change - and if they *do* change then they won't match on Equals
any more so you'd be in trouble anyway when fetching the value with an
equal (but not identical, if you see what I mean) key.

That's just my feelings on it though... certainly there are times when
the above is "good enough" and may well be the best policy available.
 
J

Jay B. Harlow [MVP - Outlook]

Edward,
In addition to Jon's comments.

By value classes, do you mean value types or reference types that have value
semantics?

I've noticed that System.Drawing.Point (a value type) always returns 0 for
GetHashCode where as Equals is overloaded to perform normal comparisons.

I would override Equals to perform equality checks on mutable types,
considering the recommendation to also override GetHashCode. Seeing as my
mutable type is not a good candidate for a key in a HashTable I probably
would not worry about overriding GetHashCode and ensuring the immutable
nature of Equals & GetHashCode...

http://msdn.microsoft.com/library/d...f/html/frlrfSystemObjectClassEqualsTopic1.asp

States: "Types that override Equals must also override GetHashCode;
otherwise, Hashtable might not work correctly."

Where as:

http://msdn.microsoft.com/library/d...ml/frlrfsystemobjectclassgethashcodetopic.asp

States: "Classes that might be used as a key in a hash table must also
override this method, because objects that are used as keys in a hash table
are required to generate their own hash code through this method."

Where as the guidelines themselves state:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconEquals.asp

"Override the GetHashCode method to allow a type to work correctly in a hash
table"
&
"Successive invocations of x.Equals(y) return the same value as long as the
objects referenced by x and y are not modified. "

In other words combining the above I know my mutable type is not a good type
for a key in a hashtable & I accept that fact!

Note: the example of Point3D.GetHashCode on the last URL is a really poor
example. They're using the Math.Pow function to calculate the hashcode, it
should be the Xor operator in VB.NET ;-)

Also I tend to overload the Equals method in addition to overriding it.

Public Class MutableClass

Public Overloads Overrides Function Equals(ByVal other As Object) As
Boolean

Public Overloads Function Equals(ByVal other As MutableClass) As Boolean

End Class

Hope this helps
Jay
 
E

Edward Diener

Jon said:
Certainly if you want them to work in a Hashtable.


Hmm... interestingly, that *is* what the documentation says for
GetHashcode, but it doesn't make much sense to me.

The idea being, I believe, that if you put an object in a Hashtable and
later search for it, the GetHashCode value remains constant so that you can
always find the object again. If the GetHashCode changed, you would never be
able to find the same object.
Interestingly, StringBuilder seems to conform to this part, but not
the "equal objects should return the same hash code" part.

I would suggest sticking to the first rule but not the second. Then if
your objects are used in a Hashtable they'll be fine so long as they
don't change - and if they *do* change then they won't match on Equals
any more so you'd be in trouble anyway when fetching the value with an
equal (but not identical, if you see what I mean) key.

Yes, I understand what you mean. But I have heard that the second rule
exists because some underlying .NET framework classes make the assumption
that GetHashCode and Equals are always in sync, although I am not sure what
those classes are. It makes sense for me that GetHashCode is immutable but
that Equals values are not and change when the underlying data of the object
changes. That is why I think it is ridiculous that GetHashCode must reflect
Equals and vice-versa. Perhaps the clarifications I received about this from
a previous posts were not correct.
 
J

Jon Skeet [C# MVP]

Edward Diener said:
The idea being, I believe, that if you put an object in a Hashtable and
later search for it, the GetHashCode value remains constant so that you can
always find the object again. If the GetHashCode changed, you would never be
able to find the same object.

But you should also be able to find objects using a key which is equal
to the original, which you can't with the StringBuilder way of doing
things...
Yes, I understand what you mean. But I have heard that the second rule
exists because some underlying .NET framework classes make the assumption
that GetHashCode and Equals are always in sync, although I am not sure what
those classes are. It makes sense for me that GetHashCode is immutable but
that Equals values are not and change when the underlying data of the object
changes. That is why I think it is ridiculous that GetHashCode must reflect
Equals and vice-versa. Perhaps the clarifications I received about this from
a previous posts were not correct.

I don't think other classes *should* rely on objects being suitable
keys for hashtables, at least without documenting it very well.
GetHashCode being immutable when Equals not being (like the
StringBuilder implementation) gives the problem described above. The
other way round gives the problem of not being able to find even the
original reference key if the object has changed.

It's a pretty inextricable problem in many ways, but so long as you
know what expects what, and document what you provide, it's not too
bad.
 
E

Edward Diener

Jay said:
Edward,
In addition to Jon's comments.

By value classes, do you mean value types or reference types that
have value semantics?

I don't see where I mentioned value classes in my original post but usually
I mean value types. In C# these are structs and in C++ they are __value
class.
I've noticed that System.Drawing.Point (a value type) always returns
0 for GetHashCode where as Equals is overloaded to perform normal
comparisons.

That sounds normal.
I would override Equals to perform equality checks on mutable types,
considering the recommendation to also override GetHashCode. Seeing
as my mutable type is not a good candidate for a key in a HashTable I
probably would not worry about overriding GetHashCode and ensuring
the immutable nature of Equals & GetHashCode...

http://msdn.microsoft.com/library/d...f/html/frlrfSystemObjectClassEqualsTopic1.asp

States: "Types that override Equals must also override GetHashCode;
otherwise, Hashtable might not work correctly."

Where as:

http://msdn.microsoft.com/library/d...ml/frlrfsystemobjectclassgethashcodetopic.asp

States: "Classes that might be used as a key in a hash table must also
override this method, because objects that are used as keys in a hash
table are required to generate their own hash code through this
method."

Where as the guidelines themselves state:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpgenref/html/cpconEquals.asp

"Override the GetHashCode method to allow a type to work correctly in
a hash table"
&
"Successive invocations of x.Equals(y) return the same value as long
as the objects referenced by x and y are not modified. "

This is the line, from the GetHashCode documentation, that is bothering me:

"Derived classes that override GetHashCode must also override Equals to
guarantee that two objects considered equal have the same hash code;
otherwise, Hashtable might not work correctly."

Now let us consider two __value class objects. I have an immutable field in
each object which I use for the hash code and do not change. I calculate
that initial value from a mutable field in the object and store it away in
an immutable field , and which I subsequently use to return my hash code so
that it always stays the same for my given object. The hashcode value of
object One is 1 and the hash code value of object two is 2. The mutable
value of object One and object Two are also different because I am
calculating, through some algorithm, the immutable field's hash code value
from them. I calculate my Equals method based on the mutable value, and they
start out being different. All is well.

Now object One's mutable value changes to be the same as the mutable value
for object two. Naturally my two objects now return true when passed into my
Equals method. But of course they continue to return different GethashCode
values since the hash code value is based on the original mutable field. But
I am breaking the injunction quoted above by me.

This means to me that mutable objects can not be placed in hash code
containers, and can not implement GetHashCode, and can not implement Equals
because the Equals documentation states:

"Types that override Equals must also override GetHashCode; otherwise,
Hashtable might not work correctly."

The only thing I have missed in my original argument that Equals and
GetHashCode are only for immutable objects is that a mutable object can be
made to follow the documentation if, for any mutable object of any given
type, the same hash code is always returned. In this way, obviously followed
by System.Drawing.Point, whether or not two mutable objects of a particular
type are Equals, they always return the same hash value. Is this then
essentially the rule for all mutable objects ?:

"All mutable objects of a given type must always return the same hash
value."

It seems to follow from my arguments and both our quotes from above. But I
have a feeling that if Hashtable expects two objects that are Equals to
return the same GetHashCode, it also clearly expects two objects that are
not equal to not return the same hash code; and of course this is not the
case with the only situation for mutable objects regarding Equals and
GetHashCode which seems acceptable from the other documentation.

So my confusion about this still exists, and I am still seeking
clarification.
 
E

Edward Diener

Jon said:
But you should also be able to find objects using a key which is equal
to the original, which you can't with the StringBuilder way of doing
things...


I don't think other classes *should* rely on objects being suitable
keys for hashtables, at least without documenting it very well.
GetHashCode being immutable when Equals not being (like the
StringBuilder implementation) gives the problem described above. The
other way round gives the problem of not being able to find even the
original reference key if the object has changed.

It is a problem if one reads the documentation. The documentation tells me
that if I implement GetHashCode I must implement Equals and vice-versa. It
also tells me that two objects returning true from Equals must return the
same GetHashCode. For mutable objects this is only possible if all mutable
objects of a given type return the same hash code. But surely this can't be
right as I am sure that hash tables expect objects that return false from
Equals to return different hash codes.

So let's say that I have mutable objects and I decide that because the
documentation is self-contradictory I can't possibly use it in a hash table,
so I don't implement GetHashCode. But then I can't implement Equals either.
So essentially for mutable objects neither GetHashCode nor Equals can ever
be implemented. Which brings me, "by a commodius vicus of recirculation back
to Howth Castle and Environs", to my original post.
It's a pretty inextricable problem in many ways, but so long as you
know what expects what, and document what you provide, it's not too
bad.

It's only bad if one wants to know what to do in order to write reusable
code that won't crash in .NET. Other than that it is fine <g> . Especially
since I am writing components for others to use, it is bad for me. The only
solution seems to be that I don't implement GetHashCode and Equals, I do
document that my components are not mean to be used in hash containers, and
that I if I want to supply some CLS equality function I choose another name
for my Equals function and document that for my end-users. All of which is
fine if I know that is the proper way to go.
 
J

Jon Skeet [C# MVP]

Edward Diener said:
I don't see where I mentioned value classes in my original post but usually
I mean value types. In C# these are structs and in C++ they are __value
class.

Ah - that changes everything, as in a Hashtable any value types will be
stored in boxed form, so they *won't* change in the hashtable. They are
effectively immutable for the purposes of the hashtable.
 
J

Jon Skeet [C# MVP]

Edward Diener said:
It is a problem if one reads the documentation. The documentation tells me
that if I implement GetHashCode I must implement Equals and vice-versa.

Well, if you want a Hashtable to work properly, yes. I don't believe
it's *possible* for a Hashtable to work properly in this situation
though.
It
also tells me that two objects returning true from Equals must return the
same GetHashCode.

.... "otherwise Hashtable might not work correctly". If you're willing
to accept that, there's no problem.
It's only bad if one wants to know what to do in order to write reusable
code that won't crash in .NET.

It won't crash - it just won't work in a Hashtable (as a key). You just
shouldn't use mutable object (which not only can change but *will*
change) as keys in a Hashtable - that's just life.
Other than that it is fine <g> . Especially
since I am writing components for others to use, it is bad for me. The only
solution seems to be that I don't implement GetHashCode and Equals, I do
document that my components are not mean to be used in hash containers, and
that I if I want to supply some CLS equality function I choose another name
for my Equals function and document that for my end-users. All of which is
fine if I know that is the proper way to go.

No, just documenting that they shouldn't be used in Hashtables if
they're going to change is enough - implement Equals and GetHashCode so
that so long as the objects *don't* change after being inserted in the
Hashtable, they'll be fine as keys.
 
E

Edward Diener

Jon said:
Ah - that changes everything, as in a Hashtable any value types will
be stored in boxed form, so they *won't* change in the hashtable.
They are effectively immutable for the purposes of the hashtable.

That's not the issue. Whether the object is immutable in the hashtable or
not is irrelevant. What's relevant is whether or not the GetHashCode value
changes or not in order to find the object in the hash table.
 
E

Edward Diener

Jon said:
Well, if you want a Hashtable to work properly, yes. I don't believe
it's *possible* for a Hashtable to work properly in this situation
though.

Essentially you are saying that only immutable objects should be put in a
hash table.
... "otherwise Hashtable might not work correctly". If you're willing
to accept that, there's no problem.


It won't crash - it just won't work in a Hashtable (as a key). You
just shouldn't use mutable object (which not only can change but
*will* change) as keys in a Hashtable - that's just life.

So you are saying for a mutable object to implement GetHashTable as never
changing and Equals as changing, but ignore the documentation about "Equals
returning true guaranteeing that GetHashTable returns the same values" since
my object, being mutable, will never be put into a hash table ? That's fine
by me as long as I know that it will not break any non-hashtable related
..NET framework implementation. But certainly the doc should explain that
better.
No, just documenting that they shouldn't be used in Hashtables if
they're going to change is enough - implement Equals and GetHashCode
so that so long as the objects *don't* change after being inserted in
the Hashtable, they'll be fine as keys.

Unless the object is immutable, the last is virtually impossible to do. I
will instead follow the guidelines that my mutable object can not be put in
a hash table.
 
J

Jon Skeet [C# MVP]

Edward Diener said:
Essentially you are saying that only immutable objects should be put in a
hash table.

Not necessarily - just objects which don't change *after* they've been
put in. And even then, only as *keys*, not as values. For the values,
it doesn't matter at all.
So you are saying for a mutable object to implement GetHashTable as never
changing and Equals as changing, but ignore the documentation about "Equals
returning true guaranteeing that GetHashTable returns the same values" since
my object, being mutable, will never be put into a hash table ? That's fine
by me as long as I know that it will not break any non-hashtable related
.NET framework implementation. But certainly the doc should explain that
better.

The docs do specify "otherwise Hashtable won't work correctly". They
don't specify "otherwise the whole world might break".
Unless the object is immutable, the last is virtually impossible to do. I
will instead follow the guidelines that my mutable object can not be put in
a hash table.

No, it's not impossible to do. Just implement them in the natural way,
using the fields. If the fields don't change, the hash code will stay
the same and Equals and GetHashCode will work exactly according to the
documentation.

It's then up to clients to read your documentation and not put the
objects in a Hashtable (as keys) and subsequently change the objects.
Often that can be very helpful - many types of object need to be
manipulated early on in their life cycle but then aren't changed.
Indeed, Hashtables themselves quite often act like that - maps that are
set up once and then just read from. Why deny your types' users that
kind of usage pattern?
 
E

Edward Diener

Jon said:
Not necessarily - just objects which don't change *after* they've been
put in. And even then, only as *keys*, not as values. For the values,
it doesn't matter at all.


The docs do specify "otherwise Hashtable won't work correctly". They
don't specify "otherwise the whole world might break".

I don't find that the docs are clear on why their is a connection between
GetHashCode and Equals. I am willing to guess that it is because of hash
code collisions, and the necessity of using Equals when more than one object
is in the same hash code bucket.
No, it's not impossible to do. Just implement them in the natural way,
using the fields. If the fields don't change, the hash code will stay
the same and Equals and GetHashCode will work exactly according to the
documentation.

OK, what about reference objects and hash tables ? Is my GetHashCode merely
the reference itself. That's fine but my object itself may change its
fields. Is this another case of not changing the object's fields once they
are put into a hash table ? Or is it that the Equals method for reference
objects should only compare the references and not the fields themselves ?

To be totally honest I find the whole business of using hash tables and .NET
objects, with GetHashCode and Equals to be flawed. I don't know what a good
solution is, but limiting all changes on objects which have been put into a
hash table seems poor technology. I haven't studied this issue before, since
standard C++ left out hash tables, but it does seem like something that
severely limits the end user.
 
J

Jon Skeet [C# MVP]

Edward Diener said:
I don't find that the docs are clear on why their is a connection between
GetHashCode and Equals. I am willing to guess that it is because of hash
code collisions, and the necessity of using Equals when more than one object
is in the same hash code bucket.

Sort of. First you find all keys with the same hashcode as the object
you've been passed, and then you check each of them with Equals to find
the right one. The dividing of keys into buckets isn't actually
relevant - it's just a performance optimisation.
OK, what about reference objects and hash tables ? Is my GetHashCode merely
the reference itself. That's fine but my object itself may change its
fields. Is this another case of not changing the object's fields once they
are put into a hash table ? Or is it that the Equals method for reference
objects should only compare the references and not the fields themselves ?

Again, you don't change the object's fields if it's been put into a
hash table as the *key*. If it's put in as the value, it doesn't matter
at all.
To be totally honest I find the whole business of using hash tables and .NET
objects, with GetHashCode and Equals to be flawed. I don't know what a good
solution is, but limiting all changes on objects which have been put into a
hash table seems poor technology. I haven't studied this issue before, since
standard C++ left out hash tables, but it does seem like something that
severely limits the end user.

You *must* remember that it's only as a key that any of this is an
issue - and I find that most of the time, keys are strings or other
common immutable types.

Do you have any use cases where someone *would* want to put a key in a
hashtable and then change it? I've *never* come across that situation,
personally.
 
E

Edward Diener

Jon said:
Sort of. First you find all keys with the same hashcode as the object
you've been passed, and then you check each of them with Equals to
find the right one. The dividing of keys into buckets isn't actually
relevant - it's just a performance optimisation.


Again, you don't change the object's fields if it's been put into a
hash table as the *key*. If it's put in as the value, it doesn't
matter at all.

I don't understand this. It's not enough that the actual hash code doesn't
change. One must make sure that values in the object correspond to the
actual hash code don't change. If the values change after the hash code is
put into a hash table, then GetHashCode and Equals are no longer in sync.
Are you suggesting that object equality using Equals only be determined by
the hash code value ? That would seem a very limiting form of object
equality. My usual interpretation of object equality is that the properties
of one object are equal to the properties of another object. Since Equals is
meant to be used outside of hash tables, such as testing whether one object
is the same as another object, limiting it to just a hash code value seems
wrong to me.
 
J

Jon Skeet [C# MVP]

Edward Diener said:
I don't understand this. It's not enough that the actual hash code doesn't
change. One must make sure that values in the object correspond to the
actual hash code don't change. If the values change after the hash code is
put into a hash table, then GetHashCode and Equals are no longer in sync.

Indeed. I wasn't talking about that value - I was talking about value
as opposed to key.

A Hashtable maps from key -> value, right? They can be, and usually
are, two entirely separate entities.

What I'm saying is that there's nothing to stop the value part here
from doing whatever it wants. It's only the GetHashCode and Equals
methods in the *key* which are important. For instance, if I do:

string key = "hello";
MyMutableObject x = new MyMutableObject();

hashtable[key]=x;

x.ChangeSomething();
x.ChangeSomethingElse();

then hashtable["hello"] will still return a reference to the
MyMutableObject you created. On the other hand, if you did:


hashtable[x] = "hello";
x.ChangeSomething();
x.ChangeSomethingElse();

then I wouldn't expect reading hashtable[x] to work, because the
hashcode would have changed by then, in my scheme of things.

This is all when talking about reference types. When talking about
value types, the key in the hashtable wouldn't have changed at all
(unless MyMutableObject contained reference types which themselves
changed) because the hashtable would have taken a copy of the object,
not just the reference.
 
J

Jay B. Harlow [MVP - Outlook]

Edward,
This is the line, from the GetHashCode documentation, that is bothering me:

"Derived classes that override GetHashCode must also override Equals to
guarantee that two objects considered equal have the same hash code;
otherwise, Hashtable might not work correctly."

You are taking that statement by itself, I am combining all three
statements, plus applying an understanding of how Equals & GetHashCode need
to work in relation to Hashtable KEYS.
Now let us consider two __value class objects. I have an immutable field in
each object which I use for the hash code and do not change. I calculate
Value types (__value class) are boxed when used as a key, you cannot really
modify a boxed value without an interface. You can however replace a boxed
value.
This means to me that mutable objects can not be placed in hash code
containers, and can not implement GetHashCode, and can not implement Equals
because the Equals documentation states:
Mutable objects may not be good KEYS, they are excellent VALUES for
hashtables. Mutable objects will be good KEYS if the fields that are used
for GetHashCode & Equals are immutable. Consider the following mutable
class, it is a good key for a HashTable, as the fields used for equality &
hash code are immutable. It doesn't matter that the value field itself can
change...

Public NotInheritable Class KeyPair

Private ReadOnly m_key1, m_key2 As Integer
Private m_value As String

Public Sub New(ByVal key1 As Integer, ByVal key2 As Integer)
m_key1 = key1
m_key2 = key2
End Sub

Public Property Value() As String
Get
Return m_value
End Get
Set(ByVal value As String)
m_value = value
End Set
End Property

Public Overrides Function GetHashCode() As Integer
Return m_key1.GetHashCode() Xor m_key2.GetHashCode()
End Function

Public Overloads Function Equals(ByVal other As KeyPair) As Boolean
Return m_key1 = other.m_key1 AndAlso m_key2 = other.m_key2
End Function

Public Overloads Overrides Function Equals(ByVal obj As Object) As
Boolean
If TypeOf obj Is KeyPair Then
Return Me.Equals(DirectCast(obj, KeyPair))
Else
Return False
End If
End Function

End Class

By mutable object I mean normal C# or VB.NET class, not a C# struct.
Remember a C# struct will be boxed when used as a key, effectively making it
immutable! Also I hope you realize that my earlier example of Point is NOT a
good key as it always returns 0 for GetHashCode, forcing all keys to be in
the first hash bucket which means you have degraded to a "simple list".

Hope this helps
Jay
 
E

Edward Diener

Jon said:
Edward Diener said:
I don't understand this. It's not enough that the actual hash code
doesn't change. One must make sure that values in the object
correspond to the actual hash code don't change. If the values
change after the hash code is put into a hash table, then
GetHashCode and Equals are no longer in sync.

Indeed. I wasn't talking about that value - I was talking about value
as opposed to key.

A Hashtable maps from key -> value, right? They can be, and usually
are, two entirely separate entities.

What I'm saying is that there's nothing to stop the value part here
from doing whatever it wants. It's only the GetHashCode and Equals
methods in the *key* which are important. For instance, if I do:

string key = "hello";
MyMutableObject x = new MyMutableObject();

hashtable[key]=x;

x.ChangeSomething();
x.ChangeSomethingElse();

then hashtable["hello"] will still return a reference to the
MyMutableObject you created. On the other hand, if you did:


hashtable[x] = "hello";
x.ChangeSomething();
x.ChangeSomethingElse();

then I wouldn't expect reading hashtable[x] to work, because the
hashcode would have changed by then, in my scheme of things.

The issue is that GetHashCode would return the same value since it is based
on an immutable field, but Equals would not. Then in case of a hash code
collision, the Equals method wouldn't be able to find the value.
This is all when talking about reference types. When talking about
value types, the key in the hashtable wouldn't have changed at all
(unless MyMutableObject contained reference types which themselves
changed) because the hashtable would have taken a copy of the object,
not just the reference.

The key in the hashtable wouldn't have changed but the original object which
serves as the key for finding the value could have changed if the object is
mutable. While this would not cause the GetHashCode to change, since it is
based on an immutable field, the Equals method to be different, thus
destroying the ability to find the hash table value when Equals must be
called because of a hash code collision.

We are going around in circles here. My original point is simply that
classes which implement GetHashCode and Equals will not be in sync once one
of the mutable values upon which Equals is based changes. Perhaps the
documentation recognizes this, or perhaps the issue is simply that one can
not use an object as a key in a hash table and have that object subsequently
change.

I am reticent to use Equals as my method to test for object equality, but
will create some other function for it in case an end-user needs to test
whether or not one of my components is "equal" to another of my components
of the same type. I continue to regard the doc that GetHashCode and Equals
must be in sync as flawed.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top