Click here to Skip to main content
15,880,608 members
Articles / Programming Languages / C#
Tip/Trick

Using reference-counting on IDisposable objects

Rate me:
Please Sign up or sign in to vote.
5.00/5 (3 votes)
25 Dec 2011CPOL1 min read 36.1K   5   4
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:


C#
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!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Netherlands Netherlands
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionCalling dispose Pin
FDW2-Dec-11 4:40
FDW2-Dec-11 4:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.