The sample described above was the subject of eloquent work with overlapping issues of removal and finalization.
When we dispose, we want:
- Dispose of all disposable member objects.
- Dispose of the base object.
- Free up unmanaged resources.
At the end we want:
- Free up unmanaged resources.
This includes the following issues:
- Disposal must be safe to call several times. Do not call
x.Dispose();x.Dispose(); - Finalization adds to the burden of garbage collection. If we avoid this, if we can, especially if we have already released unmanaged resources, we want to suppress the finalization, because it is no longer needed.
- Access to completed facilities is fraught. If the object completes, then any finalisable members (which will also deal with the same problems as our class) may or may not already be completed and, of course, will be in the completion queue. Since these objects are likely to be managed by disposable objects, and since their removal will free up their unmanaged resources, we do not want to get rid of them in this case.
The code you specify will (after adding to the finalizer that calls Dispose(false) , manage these problems. In the case of a call to Dispose() it will clear both managed and unmanaged elements and suppress termination, as well as protect several calls (in in this case, it is not thread safe.) In the case of calling the finalizer, it clears the unmanaged elements.
However, this pattern is required only for the anti-pattern combining managed and unmanaged problems in one class. A much better approach is to process all unmanaged resources through a class that only applies to that resource, whether it be SafeHandle or a separate proprietary class. Then you will have one of two patterns, of which the latter will be rare:
public class HasManagedMembers : IDisposable { public void Dispose() {
This one does not have a finalizer and is not needed.
public class HasUnmanagedResource : IDisposable { IntPtr _someRawHandle; private void CleanUp() { } public void Dispose() { CleanUp(); GC.SuppressFinalize(this); } ~HasUnmanagedResource() { CleanUp(); } }
This version, which will be much less common (not even performed on most projects), has the right to dispose exclusively using a single unmanaged resource for which the class is a wrapper and the finalizer does the same, t.
Since SafeHandle allows you to process the second template for you, you do not need it at all. In any case, the first example that I give will handle the vast majority of cases when you need to implement IDisposable . The template shown in your example should only be used for backward compatibility, for example, when you exit a class that uses it.
Jon hanna
source share