I have many classes like this - and my recommendation is to make the class sealed whenever possible. IDisposable
+ inheritance can work, but 99% of the times you don't need it - and it's easy to make mistakes with.
Unless you're writing a public API (in which case it's nice to permit people to implement your code however they wish to - i.e. to use IDisposable
), you don't need to support it.
just do:
public sealed class Foo : IDisposable
{
private StreamWriter _Writer;
public Foo(string path)
{
FileStream fileWrite = File.Open (path, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite);
try {
_Writer = new StreamWriter (fileWrite, new ASCIIEncoding());
} catch {
fileWrite.Dispose();
throw;
}
}
public void Dispose()
{
_Writer.Dispose();
}
}
Note the try...catch; technically the StreamWriter
constructor could throw an exception, in which case it never takes ownership of the FileStream
and you must dispose it yourself.
If you're really using many IDisposable
s, consider putting that code in C++/CLI: that makes all that boilerplate code for you (it transparently uses the appropriate deterministic destruction tech for both native and managed objects).
Wikipedia has a decent IDisposable sample for C++ (really, if you have many IDisposable's, C++ is actually much simpler than C#):
Wikipedia: C++/CLI Finalizers and automatic variables.
For example, implementing a "safe" disposable container in C++/CLI looks like...
public ref class MyDisposableContainer
{
auto_handle<IDisposable> kidObj;
auto_handle<IDisposable> kidObj2;
public:
MyDisposableContainer(IDisposable^ a,IDisposable^ b)
: kidObj(a), kidObj2(b)
{
Console::WriteLine("look ma, no destructor!");
}
};
This code will correctly dispose kidObj and kidObj2 even without adding a custom IDisposable implementation, and it's robust to exceptions in either Dispose implementation (not that those should occur, but still), and simple to maintain in the face of many more IDisposable
members or native resources.
Not that I'm a huge fan of C++/CLI, but for heavily resource-oriented code it's got C# beat easily, and it has absolutely brilliant interop both with managed and native code - in short, perfect glue code ;-). I tend to write 90% of my code in C#, but use C++/CLI for all interop needs (esp. if you want to call any win32 function - MarshalAs and other interop attributes all over the place are horrifying and thoroughly incomprehensible).