Home All Groups Group Topic Archive Search About

Nullable<int>, implementing GetHashCode, and thread safety

Author
17 Dec 2005 2:05 AM
dls
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;
    }
//...
}

Author
17 Dec 2005 10:11 AM
David Browne
Show quote
"dls" <d**@discussions.microsoft.com> wrote in message
news:776406B0-30B1-4C65-9B30-64A6DA73CE74@microsoft.com...
> 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

AddThis Social Bookmark Button