The recommended IDisposable pattern is here. When programming a class that uses IDisposable, generally you should use two patterns:
When implementing a sealed class that doesn't use unmanaged resources, you simply implement a Dispose method as with normal interface implementations:
public sealed class A : IDisposable
{
public void Dispose()
{
// get rid of managed resources, call Dispose on member variables...
}
}
When implementing an unsealed class, do it like this:
public class B : IDisposable
{
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
// get rid of managed resources
}
// get rid of unmanaged resources
}
// only if you use unmanaged resources directly in B
//~B()
//{
// Dispose(false);
//}
}
Notice that I haven't declared a finalizer in B
; you should only implement a finalizer if you have actual unmanaged resources to dispose. The CLR deals with finalizable objects differently to non-finalizable objects, even if SuppressFinalize
is called.
So, you shouldn't declare a finalizer unless you have to, but you give inheritors of your class a hook to call your Dispose
and implement a finalizer themselves if they use unmanaged resources directly:
public class C : B
{
private IntPtr m_Handle;
protected override void Dispose(bool disposing)
{
if (disposing)
{
// get rid of managed resources
}
ReleaseHandle(m_Handle);
base.Dispose(disposing);
}
~C() {
Dispose(false);
}
}
If you're not using unmanaged resources directly (SafeHandle
and friends doesn't count, as they declare their own finalizers), then don't implement a finalizer, as the GC deals with finalizable classes differently, even if you later suppress the finalizer. Also note that, even though B
doesn't have a finalizer, it still calls SuppressFinalize
to correctly deal with any subclasses that do implement a finalizer.
When a class implements the IDisposable interface, it means that somewhere there are some unmanaged resources that should be got rid of when you've finished using the class. The actual resources are encapsulated within the classes; you don't need to explicitly delete them. Simply calling Dispose()
or wrapping the class in a using(...) {}
will make sure any unmanaged resources are got rid of as necessary.
I had a look at the code in ThreadLocal<T>
to see what the current Dispose
is doing and it appears to be a lot of voodoo. Obviously disposing of thread-related stuff.
But it doesn't dispose of the values if T
itself is disposable.
Now, I have a solution - a ThreadLocalDisposables<T>
class, but before I give the full definition it's worth thinking about what should happen if you wrote this code:
var tl = new ThreadLocalDisposables<IExpensiveDisposableResource>();
tl.Value = myEdr1;
tl.Value = myEdr2;
tl.Dispose();
Should both myEdr1
& myEdr2
both be disposed? Or just myEdr2
? Or should myEdr1
be disposed when myEdr2
was assigned?
It's not clear to me what the semantics should be.
It is clear to me, however, that if I wrote this code:
var tl = new ThreadLocalDisposables<IExpensiveDisposableResource>(
() => new ExpensiveDisposableResource());
tl.Value.DoSomething();
tl.Dispose();
Then I would expect that the resource created by the factory for each thread should be disposed of.
So I'm not going to allow the direct assignment of the disposable value for ThreadLocalDisposables
and only allow the factory constructor.
Here's ThreadLocalDisposables
:
public class ThreadLocalDisposables<T> : IDisposable
where T : IDisposable
{
private ThreadLocal<T> _threadLocal = null;
private ConcurrentBag<T> _values = new ConcurrentBag<T>();
public ThreadLocalDisposables(Func<T> valueFactory)
{
_threadLocal = new ThreadLocal<T>(() =>
{
var value = valueFactory();
_values.Add(value);
return value;
});
}
public void Dispose()
{
_threadLocal.Dispose();
Array.ForEach(_values.ToArray(), t => t.Dispose());
}
public override string ToString()
{
return _threadLocal.ToString();
}
public bool IsValueCreated
{
get { return _threadLocal.IsValueCreated; }
}
public T Value
{
get { return _threadLocal.Value; }
}
}
Does this help?
Best Answer
It won't work because the
Dispose
method onBinaryReader
has been explicitly implemented.Instead of being implicitly implemented, as in:
...it has been explicitly implemented, as in:
...which means it can only be accessed via the
IDisposable
interface. Therefore, you have to cast the instance toIDisposable
first.