65.9K
CodeProject is changing. Read more.
Home

Using reference-counting on IDisposable objects

starIconstarIconstarIconstarIconstarIcon

5.00/5 (3 votes)

Dec 1, 2011

CPOL

1 min read

viewsIcon

37284

Reference Counting implementation using Extension Methods

In my current C# project, I have a need to share some IDisposable objects between various classes of unknown lifetime. After searching the internet without success, I decided to come up with my own Reference Counting implementation using Extension Methods:

using System;
using System.Runtime.CompilerServices;

public static class IDisposableExtensions
{
    /// <summary>
    /// Values in a ConditionalWeakTable need to be a reference type,
    /// so box the refcount int in a class.
    /// </summary>
    private class RefCount
    {
        public int refCount;
    }

    private static ConditionalWeakTable<IDisposable, RefCount> refCounts = 
                     new ConditionalWeakTable<IDisposable, RefCount>();

    /// <summary>
    /// Extension method for IDisposable.
    /// Increments the refCount for the given IDisposable object.
    /// Note: newly instantiated objects don't automatically have a refCount of 1!
    /// If you wish to use ref-counting, always call retain() whenever you want
    /// to take ownership of an object.
    /// </summary>
    /// <remarks>This method is thread-safe.</remarks>
    /// <param name="disposable">The disposable that should be retained.</param>
    public static void Retain(this IDisposable disposable)
    {
        lock (refCounts)
        {
            RefCount refCount = refCounts.GetOrCreateValue(disposable);
            refCount.refCount++;
        }
    }

    /// <summary>
    /// Extension method for IDisposable.
    /// Decrements the refCount for the given disposable.
    /// </summary>
    /// <remarks>This method is thread-safe.</remarks>
    /// <param name="disposable">The disposable to release.</param>
    public static void Release(this IDisposable disposable)
    {
        lock (refCounts)
        {
            RefCount refCount = refCounts.GetOrCreateValue(disposable);
            if (refCount.refCount > 0)
            {
                refCount.refCount--;
                if (refCount.refCount == 0)
                {
                    refCounts.Remove(disposable);
                    disposable.Dispose();
                }
            }
            else
            {
                // Retain() was never called, so assume there is only
                // one reference, which is now calling Release()
                disposable.Dispose();
            }
         }
    }
}

Note that, while this works fine, it is of course a very inefficient implementation of an already inefficient mechanism. Luckily, there are likely only a handful of objects requiring this. Use using{} blocks whenever you can! Also, always consider whether it is worth the extra hassle of using explicit reference-counting, or whether you would be better off just letting the garbage collector handle the cleanup. If disposable objects are well written, they will have a destructor as well.

The nice thing is, however, that it works with any old IDisposable implementing object, regardless of its origin (as long as it hasn't been casted to Object, of course).

Warning: Newly created objects don't automatically get a refcount of 1, as you might expect. Rather, if you want to use reference counting on any object, you'll have to call Retain() explicitly. Also, all the usual advice around reference-counted objects apply, i.e., don't create reference loops.

Update: Updated the code and text to incorporate some of Paulo Zemek's excellent feedback. His comment seems to have vanished, but thanks anyway!