I would not have to call the Dispose object for myself, but if necessary I would have a constructor. I would also like to make this cleaning as simple as possible. Given your example, I would rather write something like:
internal sealed class Library : IDisposable { IntPtr _libPtr; // Or better yet, can we use or derive from SafeHandle? public Library(string dllPath) { _libPtr = NativeMethods.LoadLibrary(dllPath); if(_libPtr == IntPtr.Zero) { GC.SuppressFinalize(this); throw new DllNotFoundException("Library Load Failed"); } } private void Release() { if(_libPtr != IntPtr.Zero) NativeMethods.FreeLibrary(_libPtr); _libPtr = IntPtr.Zero; // avoid double free even if a caller double-disposes. } public void Dispose() { Release(); GC.SuppressFinalize(this); } ~Library() { Release(); } public IntPtr GetProcAddress(string functionName) { if(_libPtr == IntPtr.Zero) throw new ObjectDisposedException(); IntPtr funcPtr = NativeMethods.GetProcAddress(_libPtr, functionName); if(_funcPtr == IntPtr.Zero) throw new Exception("Error binding function."); return _funcPtr; } }
It is still nice and simple. Either this object was successfully created and can be released by the code that called it, or it does not need to be cleaned. We can even prevent the finalization of no-op, just to be enjoyable. The main thing is that there is nothing that needs to be cleaned up, created after the last thing that could reasonably go wrong.
And then:
public sealed class MyClass : IDisposable { private readonly Library _lib; private readonly IntPtr _funcPtr; public MyClass(string dllPath) { _lib = new Library(dllPath);
Again, I can provide cleanup, but I do not call this.Dispose(); While this.Dispose() can do the same trick, I prefer to have the field that I clear explicitly in the same method (constructor here) that installed it but failed to do all of its work. Firstly, the only place where a partially constructed object can be is the constructor, so the only place I need to consider a partially constructed object is in the constructor; I made it the invariant of the rest of the class that _lib not null.
Suppose functions were to be released separately from libraries, just to have a more complex example. Then I would also _funcPtr to follow a simplifying rule; either the class has one unmanaged resource that it cleans up with Dispose() and a finalizer, or it has one or more IDisposable fields that it cleans up with Dispose or it doesnβt need to be deleted, but it never combines above.
internal sealed class Function : IDisposable { IntPtr _funcPtr;
And then MyClass will be:
public sealed class MyClass : IDisposable { private Library _lib; private Function _func; public MyClass(string dllPath) { _lib = new Library(dllPath);
This makes the constructor a bit more verbose, and I would rather avoid two things that might go wrong to affect the two things that need to be cleaned up first. It, although it reflects why I like cleaning, to be explicit in relation to different fields; Perhaps I want to clear both fields or only one, depending on where the exception will occur.