Proper use of the IDisposable interface

后端 未结 19 3325
情深已故
情深已故 2020-11-21 04:05

I know from reading the Microsoft documentation that the \"primary\" use of the IDisposable interface is to clean up unmanaged resources.

To me, \"unman

相关标签:
19条回答
  • 2020-11-21 04:41

    IDisposable is often used to exploit the using statement and take advantage of an easy way to do deterministic cleanup of managed objects.

    public class LoggingContext : IDisposable {
        public Finicky(string name) {
            Log.Write("Entering Log Context {0}", name);
            Log.Indent();
        }
        public void Dispose() {
            Log.Outdent();
        }
    
        public static void Main() {
            Log.Write("Some initial stuff.");
            try {
                using(new LoggingContext()) {
                    Log.Write("Some stuff inside the context.");
                    throw new Exception();
                }
            } catch {
                Log.Write("Man, that was a heavy exception caught from inside a child logging context!");
            } finally {
                Log.Write("Some final stuff.");
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-21 04:41

    Apart from its primary use as a way to control the lifetime of system resources (completely covered by the awesome answer of Ian, kudos!), the IDisposable/using combo can also be used to scope the state change of (critical) global resources: the console, the threads, the process, any global object like an application instance.

    I've written an article about this pattern: http://pragmateek.com/c-scope-your-global-state-changes-with-idisposable-and-the-using-statement/

    It illustrates how you can protect some often used global state in a reusable and readable manner: console colors, current thread culture, Excel application object properties...

    0 讨论(0)
  • 2020-11-21 04:43

    I won't repeat the usual stuff about Using or freeing un-managed resources, that has all been covered. But I would like to point out what seems a common misconception.
    Given the following code

    Public Class LargeStuff
      Implements IDisposable
      Private _Large as string()
    
      'Some strange code that means _Large now contains several million long strings.
    
      Public Sub Dispose() Implements IDisposable.Dispose
        _Large=Nothing
      End Sub
    

    I realise that the Disposable implementation does not follow current guidelines, but hopefully you all get the idea.
    Now, when Dispose is called, how much memory gets freed?

    Answer: None.
    Calling Dispose can release unmanaged resources, it CANNOT reclaim managed memory, only the GC can do that. Thats not to say that the above isn't a good idea, following the above pattern is still a good idea in fact. Once Dispose has been run, there is nothing stopping the GC re-claiming the memory that was being used by _Large, even though the instance of LargeStuff may still be in scope. The strings in _Large may also be in gen 0 but the instance of LargeStuff might be gen 2, so again, memory would be re-claimed sooner.
    There is no point in adding a finaliser to call the Dispose method shown above though. That will just DELAY the re-claiming of memory to allow the finaliser to run.

    0 讨论(0)
  • 2020-11-21 04:44

    I see a lot of answers have shifted to talk about using IDisposable for both managed and unmanaged resources. I'd suggest this article as one of the best explanations that I've found for how IDisposable should actually be used.

    https://www.codeproject.com/Articles/29534/IDisposable-What-Your-Mother-Never-Told-You-About

    For the actual question; should you use IDisposable to clean up managed objects that are taking up a lot of memory the short answer would be no. The reason is that once you Dispose of an IDisposable you should be letting it go out of scope. At that point any referenced child objects are also out of scope and will get collected.

    The only real exception to this would be if you have a lot of memory tied up in managed objects and you've blocked that thread waiting for some operation to complete. If those objects where not going to be needed after that call completed then setting those references to null might allow the garbage collector to collect them sooner. But that scenario would represent bad code that needed to be refactored - not a use case of IDisposable.

    0 讨论(0)
  • 2020-11-21 04:46

    There are things that the Dispose() operation does in the example code that might have an effect that would not occur due to a normal GC of the MyCollection object.

    If the objects referenced by _theList or _theDict are referred to by other objects, then that List<> or Dictionary<> object will not be subject to collection but will suddenly have no contents. If there were no Dispose() operation as in the example, those collections would still contain their contents.

    Of course, if this were the situation I would call it a broken design - I'm just pointing out (pedantically, I suppose) that the Dispose() operation might not be completely redundant, depending on whether there are other uses of the List<> or Dictionary<> that are not shown in the fragment.

    0 讨论(0)
  • 2020-11-21 04:50

    If MyCollection is going to be garbage collected anyway, then you shouldn't need to dispose it. Doing so will just churn the CPU more than necessary, and may even invalidate some pre-calculated analysis that the garbage collector has already performed.

    I use IDisposable to do things like ensure threads are disposed correctly, along with unmanaged resources.

    EDIT In response to Scott's comment:

    The only time the GC performance metrics are affected is when a call the [sic] GC.Collect() is made"

    Conceptually, the GC maintains a view of the object reference graph, and all references to it from the stack frames of threads. This heap can be quite large and span many pages of memory. As an optimisation, the GC caches its analysis of pages that are unlikely to change very often to avoid rescanning the page unnecessarily. The GC receives notification from the kernel when data in a page changes, so it knows that the page is dirty and requires a rescan. If the collection is in Gen0 then it's likely that other things in the page are changing too, but this is less likely in Gen1 and Gen2. Anecdotally, these hooks were not available in Mac OS X for the team who ported the GC to Mac in order to get the Silverlight plug-in working on that platform.

    Another point against unnecessary disposal of resources: imagine a situation where a process is unloading. Imagine also that the process has been running for some time. Chances are that many of that process's memory pages have been swapped to disk. At the very least they're no longer in L1 or L2 cache. In such a situation there is no point for an application that's unloading to swap all those data and code pages back into memory to 'release' resources that are going to be released by the operating system anyway when the process terminates. This applies to managed and even certain unmanaged resources. Only resources that keep non-background threads alive must be disposed, otherwise the process will remain alive.

    Now, during normal execution there are ephemeral resources that must be cleaned up correctly (as @fezmonkey points out database connections, sockets, window handles) to avoid unmanaged memory leaks. These are the kinds of things that have to be disposed. If you create some class that owns a thread (and by owns I mean that it created it and therefore is responsible for ensuring it stops, at least by my coding style), then that class most likely must implement IDisposable and tear down the thread during Dispose.

    The .NET framework uses the IDisposable interface as a signal, even warning, to developers that the this class must be disposed. I can't think of any types in the framework that implement IDisposable (excluding explicit interface implementations) where disposal is optional.

    0 讨论(0)
提交回复
热议问题