Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>There's lots of good discussion already here, and I'm a little late to the party, but I wanted to add a few points myself.</p> <ul> <li>The Garbage collecter will never directly execute a Dispose method for you.</li> <li>The GC <em>will</em> execute finalizers when it feels like it.</li> <li>One common pattern that is used for objects that have a finalizer is to have it call a method which is by convention defined as Dispose(bool disposing) passing false to indicate that the call was made due to finalization rather than an explicit Dispose call.</li> <li>This is because it is not safe to make any assumptions about other managed objects while finalizing an object (they may have already been finalized).</li> </ul> <pre><code> class SomeObject : IDisposable { IntPtr _SomeNativeHandle; FileStream _SomeFileStream; // Something useful here ~ SomeObject() { Dispose(false); } public void Dispose() { Dispose(true); } protected virtual void Dispose(bool disposing) { if(disposing) { GC.SuppressFinalize(this); //Because the object was explicitly disposed, there will be no need to //run the finalizer. Suppressing it reduces pressure on the GC //The managed reference to an IDisposable is disposed only if the _SomeFileStream.Dispose(); } //Regardless, clean up the native handle ourselves. Because it is simple a member // of the current instance, the GC can't have done anything to it, // and this is the onlyplace to safely clean up if(IntPtr.Zero != _SomeNativeHandle) { NativeMethods.CloseHandle(_SomeNativeHandle); _SomeNativeHandle = IntPtr.Zero; } } } </code></pre> <p>That's the simple version, but there are a lot of nuances that can trip you up on this pattern. </p> <ul> <li>The contract for IDisposable.Dispose indicates that it must be safe to call multiple times (calling Dispose on an object that was already disposed should do nothing)</li> <li>It can get very complicated to properly manage an inheritance hierarchy of disposable objects, especially if different layers introduce new Disposable and unmanaged resources. In the pattern above Dispose(bool) is virtual to allow it to be overridden so that it can be managed, but I find it to be error-prone.</li> </ul> <p>In my opinion, it is much better to completely avoid having any types that directly contain both disposable references and native resources that may require finalization. SafeHandles provide a very clean way of doing this by encapsulating native resources into disposable that internally provide their own finalization (along with a number of other benefits like removing the window during P/Invoke where a native handle could be lost due to an asynchronous exception).</p> <p>Simply defining a SafeHandle makes this Trivial:</p> <pre><code> private class SomeSafeHandle : SafeHandleZeroOrMinusOneIsInvalid { public SomeSafeHandle() : base(true) { } protected override bool ReleaseHandle() { return NativeMethods.CloseHandle(handle); } } </code></pre> <p>Allows you to simplify the containing type to:</p> <pre><code> class SomeObject : IDisposable { SomeSafeHandle _SomeSafeHandle; FileStream _SomeFileStream; // Something useful here public virtual void Dispose() { _SomeSafeHandle.Dispose(); _SomeFileStream.Dispose(); } } </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload