Click here to Skip to main content
Click here to Skip to main content

Tagged as

Using reference-counting on IDisposable objects

, 25 Dec 2011 CPOL
Rate this:
Please Sign up or sign in to vote.
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!

License

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

Share

About the Author

Stefan Riemens

Netherlands Netherlands
No Biography provided

Comments and Discussions

 
QuestionCalling dispose PinmemberFDW2-Dec-11 5:40 
Dispose() should not be called while de lock has not been released.
QuestionI like it PinmemberFDW2-Dec-11 2:15 

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

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

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141223.1 | Last Updated 25 Dec 2011
Article Copyright 2011 by Stefan Riemens
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid