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
{
private class RefCount
{
public int refCount;
}
private static ConditionalWeakTable<IDisposable, RefCount> refCounts =
new ConditionalWeakTable<IDisposable, RefCount>();
public static void Retain(this IDisposable disposable)
{
lock (refCounts)
{
RefCount refCount = refCounts.GetOrCreateValue(disposable);
refCount.refCount++;
}
}
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
{
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!