Equatable Weak References

In a previous post I described a WeakReference class for providing strongly-typed WeakReference objects.

GitHub has the latest version of EquatableWeakReference

One problem with the previous WeakReference class is being able to use and find it within the various collection classes. This is because one WeakReference is not equal to another WeakReference class.

Overriding the Equals method fixes this problem at first glance however also reveals another issue.

If you override Equals you should also override the GetHashCode method so that two objects that equal each other return the same hash code. This is because some of the collection classes use hash codes to efficiently lookup items within their collection.

Normally a hash code would be calculated from the various data items that comprise the class but in our case we really only have one to go on – the Target object itself. This raises two more issues:

  1. The hash code should not change over the objects lifetime – difficult when your Target object can be changed.
  2. The hash code should be stored because the Target object might well be collected by the GC – after all that’s what this class is all about.

This doesn’t leave us with many choices at all.

We must grab the hash code from the Target object within our constructor and store it for subsequent retrieval.

Here is EquatableWeakReference with the usual disclaimers as to it’s suitability for any purpose.

using System;
using System.Runtime.InteropServices;

public class EquatableWeakReference<T> : IEquatable<EquatableWeakReference<T>>, IDisposable where T : class
{
    protected GCHandle handle;
    protected int hashCode;

    public EquatableWeakReference(T target) {
        if (target == null)
            throw new ArgumentNullException("target");
        hashCode = target.GetHashCode();
        InitializeHandle(target);
    }

    protected virtual void InitializeHandle(T target) {
        handle = GCHandle.Alloc(target, GCHandleType.Weak);
    }

    ~EquatableWeakReference() {
        Dispose();
    }

    public void Dispose() {
        handle.Free();
        GC.SuppressFinalize(this);
    }

    public virtual bool IsAlive {
        get { return (handle.Target != null); }
    }

    public virtual T Target {
        get {
            object o = handle.Target;
            if ((o == null) || (!(o is T)))
               return null;
            else
               return (T)o;
            }
    }

    public override bool Equals(object other) {
        if (other is EquatableWeakReference<T>)
            return Equals((EquatableWeakReference<T>)other);
        else
            return false;
    }

    public override int GetHashCode() {
        return hashCode;
    }

    public bool Equals(EquatableWeakReference<T> other) {
        return ReferenceEquals(other.Target, this.Target);
    }
}

[)amien

0 responses