0

Suppose that I have the following classes:

class Foo : IDisposable
{
  public void Dispose()
  {
    bar.Dispose();
  }

  private Bar bar = new Bar();
}

class Bar : IDisposable
{
  public void Dispose()
  {
    baz.Dispose();
  }

  private SomeThirdPartyLibraryClass baz = new SomeThirdPartyLibraryClass();
}

This code works well when using using statement:

using (Foo foo = new Foo())
{
  // ...
}
// All resources should be disposed at this time

However, if for some reason user of this class forgot to use using statement, resources will never be disposed.

According to MSDN, I should implement disposable pattern in the following manner:

class Foo : IDisposable
{
  public void Dispose()
  {
    Dispose(true);
    GC.SuppressFinalize(this);
  }

  protected virtual void Dispose(bool disposing)
  {
    if (disposed)
      return; 

    if (disposing) {
      // Free any managed objects here.
      //
    }

    // Free any unmanaged objects here.
    //
    disposed = true;
  }

  ~Foo()
  {
    Dispose(false);
  }

  bool disposed = false;
  private Bar bar = new Bar();
}

(the same goes for Bar)

But where exactly should I place bar.Dispose(); and baz.Dispose(); code?

Should it be under "managed cleanup" section or under "unmanaged cleanup" section?

As the name suggests, I don't know the implementation of SomeThirdPartyLibraryClass (and anyway, it can be changed over time).

What should I do then?

FrozenHeart
  • 19,844
  • 33
  • 126
  • 242
  • Where on MSDN does it advise to use a finalizer as part of a generic IDisposable implementation? – stuartd Jun 29 '16 at 12:17
  • Possible duplicate of [Is it possible to force the use of "using" for disposable classes?](http://stackoverflow.com/questions/2675504/is-it-possible-to-force-the-use-of-using-for-disposable-classes) Since you cannot modify your third party library. It also contains some wrapping solutions. – Panda Jun 29 '16 at 12:17
  • @stuartd https://msdn.microsoft.com/ru-ru/library/fs2xkftw(v=vs.110).aspx. No? – FrozenHeart Jun 29 '16 at 12:18
  • All .NET objects are managed. It goes under "free any managed objects here". Only if you are personally incorporating unmanaged resources (something nobody has any reason to do, since everything you could want to use has been wrapped in appropriate classes for you already) does anything go under "free unmanaged resources". Third-party code will take care of unmanaged resources *it* allocates in its *own* `Dispose(false)` path. – Jeroen Mostert Jun 29 '16 at 12:19
  • @Panda No, it's not a duplicate of that question. I don't want to force my users to use `using` statement. I just want to be sure where should I place the corresponding `Dispose` calls – FrozenHeart Jun 29 '16 at 12:19
  • @Jeroen Mostert So, should I just skip the "Free any unmanaged objects here" path for both `Bar` and `Baz` and that's it? – FrozenHeart Jun 29 '16 at 12:21
  • @FrozenHeart I think your 1st snippet was correct, but wrapped in "Dan Bryant"'s solution in the provided link, you'll be *sure* to dispose these objects. – Panda Jun 29 '16 at 12:22
  • 1
    @FrozenHeart as that article suggests implementing a finalizer is advisable **if you have unmanaged resources** which isn't a common use case. See [Dispose, Finalization, and Resource Management](http://joeduffyblog.com/2005/04/08/dg-update-dispose-finalization-and-resource-management/) for much more info – stuartd Jun 29 '16 at 12:33
  • @stuartd So in my case I can skip the `Dispose(bool)` methods and finalizers and just dispose my data members and call `GC.SuppressFinalize(this);` directly in the `Dispose()` method? – FrozenHeart Jun 29 '16 at 12:38
  • @FrozenHeart not sure - from that link: _"The public void Dispose() method should be left final (i.e. not marked virtual) and consist of only two operations: a virtual call to Dispose(true) and a call to GC.SuppressFinalize(this), in that order. The call to SuppressFinalize should only occur if Dispose(true) executes successfully—thus, you should not place the call inside a finally block."_ – stuartd Jun 29 '16 at 12:40

2 Answers2

1

But where exactly should I place bar.Dispose(); and baz.Dispose(); code?

As your third party library seems to be managed code, it would go in the "managed cleanup" section.

nvoigt
  • 75,013
  • 26
  • 93
  • 142
  • So in my case I can skip the `Dispose(bool)` methods and finalizers and just dispose my data members and call `GC.SuppressFinalize(this);` directly in the `Dispose()` method? – FrozenHeart Jun 29 '16 at 12:38
  • 1
    I don't know why you'd call `GC.SuppressFinalize(this);` if you use the simple way. The simple way is fine though. Just add a call to your managed disposable resource in your own Dispose implementation. – nvoigt Jun 29 '16 at 12:41
  • @nvoigt: Calling either `GC.SuppressFinalize(this)` or `GC.KeepAlive(this)` will guard against some rare but hard-to-diagnose problems if the class holds any references to objects with finalizers by ensuring that the finalizers can't run until after disposal is complete. – supercat Jul 07 '16 at 18:43
1

You should be calling Dispose of bar and baz under the managed section of Dispose because for your class they are managed objects. If bar and baz have something unmanaged then that should get cleaned up by their respective classes in their Dispose/Finalize implementation.

Deepak Bhatia
  • 1,090
  • 1
  • 8
  • 13
  • So in my case I can skip the `Dispose(bool)` methods and finalizers and just dispose my data members and call `GC.SuppressFinalize(this);` directly in the `Dispose()` method? – FrozenHeart Jun 29 '16 at 12:38
  • 1
    Yes, that make sense, but as you are not implementing Finalize now, there is no need for SupressFinalize. – Deepak Bhatia Jun 29 '16 at 12:42
  • @DeepakBhatia: If a class holding references to objects with finalizers doesn't include access `this` the end of its `Dispose` method in a way that can't be reordered, those finalizers could in some cases start running before the `Dispose` function is finished. Calling `GC.KeepAlive(this)` or `GC.SuppressFinalize(this)` is a cheap way to ensure that neither `this` nor anything to which `this` holds a reference will be eligible for collection until disposal is complete. – supercat Jul 07 '16 at 18:40