Thursday, January 3, 2008

Generic weak reference

I'm sure I'm not the first person who's found that there is no generic version of the System.WeakReference class. I searched and did find some implementation, but I found it not good enough. First of all it didn't inherit from WeakReference which is a major drawback since it can't be passed where a WeakReference is expected as an argument.

Also I thought it would be nice to be able to implicitly cast between a WeakReference<T> and a T as follows:

WeakReference<string> r = new WeakReference<string>("doh!");  
string s = r; // The variable s now holds the value "doh!".

(To be honest a weak reference to a string-object would in most cases be quite useless but this is just an example).

I'm not sure how well known the cast operators of .Net are, but they sure are handy, especially for this kind of generic classes, check out the implementation of Nullable<T> for examples of both the implicit and the explicit cast operator.

The implementation is simple enough as follows:

/// <summary> 
/// Represents a weak reference, which references an object while still allowing   
/// that object to be reclaimed by garbage collection.    
/// </summary>    
/// <typeparam name="T">The type of the object that is referenced.</typeparam>    
[Serializable]    
public class WeakReference<T>        
    : WeakReference where T : class    
{       
    /// <summary>        
    /// Initializes a new instance of the Minimal.WeakReference{T} class, referencing        
    /// the specified object.        
    /// </summary>        
    /// <param name="target">The object to reference.</param>        
    public WeakReference(T target)            
        : base(target) { } 
    /// <summary>        
    /// Initializes a new instance of the WeakReference{T} class, referencing 
    /// the specified object and using the specified resurrection tracking. 
    /// </summary>        
    /// <param name="target">An object to track.</param> 
    /// <param name="trackResurrection">Indicates when to stop tracking the object. If true, the object is tracked 
    /// after finalization; if false, the object is only tracked until finalization.</param> 
    public WeakReference(T target, bool trackResurrection) 
        : base(target, trackResurrection) { } 
    protected WeakReference(SerializationInfo info, StreamingContext context) 
        : base(info, context) { } 
    /// <summary> 
    /// Gets or sets the object (the target) referenced by the current WeakReference{T} 
    /// object. 
    /// </summary> 
    public new T Target 
    { 
        get 
        { 
            return (T)base.Target; 
        } 
        set 
        { 
            base.Target = value; 
        } 
    } 
    /// <summary> 
    /// Casts an object of the type T to a weak reference 
    /// of T. 
    /// </summary> 
    public static implicit operator WeakReference<T>(T target)        
    { 
        if (target == null)            
        { 
            throw new ArgumentNullException("target"); 
        } 
        return new WeakReference<T>(target); 
    } 
    /// <summary> 
    /// Casts a weak reference to an object of the type the 
    /// reference represents. 
    /// </summary> 
    public static implicit operator T(WeakReference<T> reference) 
    { 
        if (reference != null) 
        { 
            return reference.Target; 
        } 
        else 
        { 
            return null; 
        } 
    } 
}

12 comments:

  1. Awesome. You rock. Thanks

    ReplyDelete
  2. Great stuff!

    I kicked you (a good thing ;)

    http://www.dotnetkicks.com/csharp/A_generic_implementation_of_the_WeakReference_object

    ReplyDelete
  3. A clean implementation but i have to say I don't think it's a good idea to enable what you're trying to enable in the first place.

    The casting operator makes it impossible to see the importance of what is being performed during an assignment - ie switching from a weak to strong reference. This will make code harder to maintain.

    ReplyDelete
  4. @Dave

    What I'm trying to enable in the first place is to make the weak reference generic, the cast operators are just icing on the cake. Maybe you're right and the implicit casting is a smell but just leave them out in your implementation, the class is still just as valid.

    ReplyDelete
  5. Great job! I just wonder if it is possible to add a function to return whether the object still has strong root. The function IsAlive() can only check whether the object is accessible or not. I have a special case that I don't want to use the object which is a candidate for garbage collection even though it hasn't been collected yet.

    ReplyDelete
  6. @Tao

    I can't see that there is a way to do that. You'd have to the same job that the garbage collector does to traverse the object graph and see if it has a strong root. The only possibility would be if there's a way to ask the GC about this, as far as I know there's no such way.

    ReplyDelete
  7. @Tao

    Actually there is a way to do what you want, just force a garbage collection before you try to access the weak reference.

    GC.Collect();
    var value = weakReference.Target;

    if (value != null)
    {
    // Do something.
    }

    ReplyDelete
  8. Thanks. That's a nice simple implementation of a generic weak reference. Personally, I'm undecided about the implicit cast operators.

    @Patrik
    You should never "just force a garbage collection". It is something that should be done only in very special circumstances - certainly not as a matter of course in order to access a property or simple function result. By repeatedly calling GC.Collect you cause all sorts of problems with the garbage collection's optimisation approach (object generations) as well as the significant cost involved in doing a collection.

    ReplyDelete
  9. @xap

    I don't say that forcing Garbage collection is a good solution. I say that it's the only solution to what Tao asks for.

    ReplyDelete
  10. I personally would prefer to cast to the weak-reference explicitly, as this is kinda "dangerous". But I love the rest :)

    ReplyDelete
  11. Why not use a *struct* and save a whole 48 bytes per weak reference? (that’s how much your extra wrapper takes on a 64-bit machine).

    ReplyDelete
    Replies
    1. It wouldn't be possible to use a struct since the generic class inherits the WeakReference-class from the framework.

      Delete