Nullable<int>, implementing GetHashCode, and thread safety

  • Thread starter Thread starter Guest
  • Start date Start date
G

Guest

The VS2005 documentation for Object.GetHashCode has three notes to
implementors:

*blockquote*
If two objects of the same type represent the same value, the hash function
must return the same constant value for either object.

For the best performance, a hash function must generate a random
distribution for all input.

The hash function must return exactly the same value regardless of any
changes that are made to the object.
*/blockquote*

So in a reference type that isn't immutable satisfying the third requirement
seems to require caching the hash code the first time it's queried for. This
seems like a good place for Nullable<int>.

However, if there's a competing requirement for the reference type to be
thread-safe, the cached hash code needs to be initially assigned in a thread
safe manner. To avoid the thread safety problems with double checked locking,
there will be a locking cost associated with initially creating and caching
the hash code. To minimize the cost, and since the hash code will be
int-sized, it'd be nice to take advantage of the System.Threading.Interlocked
class's methods.

Hmm, but Interlocked.CompareExchange can't handle the following:

Nullable<int> alpha = null;
int value = 1;
Interlocked.CompareExchange(alpha,value,null);

Rats. The nullable type would have seemed cleaner, but boxing an int into an
object (below) still works.

Is this the best way to go?
How bad would the overhead of locking a private instance field and using a
nullable type be by comparison?
Is there something obvious with Nullable<int> that I'm missing that would
support this?
Should Interlocked get overrides to handle Nullable<T>?
Could such overrides avoid the same overhead that locking a private instance
field would face?

class Triple<T1, T2, T3>
{
private Object hashCode;
private T1 first;
private T2 second;
private T3 third;
//...
public override int GetHashCode(){
if (this.hashCode == null){
int firstHash = (!Object.ReferenceEquals(this.first,null)) ?
this.first.GetHashCode() : 0;
int secondHash = (!Object.ReferenceEquals(this.second,null)) ?
this.second.GetHashCode() : 0;
int thirdHash = (!Object.ReferenceEquals(this.third,null)) ?
this.third.GetHashCode() : 0;
Interlocked.CompareExchange(ref this.hashCode,
(Object)(firstHash ^ secondHash ^ thirdHash), null);
}
return (int) this.hashCode;
}
//...
}
 
dls said:
The VS2005 documentation for Object.GetHashCode has three notes to
implementors:

*blockquote*
If two objects of the same type represent the same value, the hash
function
must return the same constant value for either object.

For the best performance, a hash function must generate a random
distribution for all input.

The hash function must return exactly the same value regardless of any
changes that are made to the object.
*/blockquote*

So in a reference type that isn't immutable satisfying the third
requirement
seems to require caching the hash code the first time it's queried for.
This
seems like a good place for Nullable<int>.

However, if there's a competing requirement for the reference type to be
thread-safe, the cached hash code needs to be initially assigned in a
thread
safe manner. To avoid the thread safety problems with double checked
locking,
there will be a locking cost associated with initially creating and
caching
the hash code. To minimize the cost, and since the hash code will be
int-sized, it'd be nice to take advantage of the
System.Threading.Interlocked
class's methods.

If your object has no readonly data and you are not overriding ==, then you
should probably just use the default implentation of Object.GetHashCode().
It has all the requisite guarantees.

.. . .
class Triple<T1, T2, T3>
{
private T1 first;
private T2 second;
private T3 third;
//..

In this particular case you might just rely on the GetHashCode for the
aggregated types and XOR them together.

David
 
Back
Top