1

I want a method to return an unmanaged resource and later in the program dispose of that resource. Does the following implementation do what I intend ?

class DocxManager
{
    // DocX is the unmanaged resource
    public Docx createDocX(string pathToFile)
    {
       return new DocX(pathToFile);
    }

    public void makeChangesAndSaveDocX(DocX wordDoc)
    {
       wordDoc.Save();
       // IS THIS WAY THE UNMANAGED RESOURCE FREED CORRECTLY AND NO MEMORY LEAK ?
       ((IDisposable)wordDoc).Dispose();
    }
}
Ciprian Dragoe
  • 340
  • 2
  • 14
  • Does Docx implement IDisposable? – Master117 May 19 '16 at 07:30
  • 1
    Like @Master117 said, `DocX` class must implement `IDisposable` interface. Check this MSDN documentation: https://msdn.microsoft.com/en-us/library/b1yfkh5e(v=vs.110).aspx – erikvimz May 19 '16 at 07:36
  • Yes it does, documentation states: public class DocX : Container, IDisposable – Ciprian Dragoe May 19 '16 at 07:44
  • 1
    Handing out an object reference to code external to your class and then killing that object tends to give that external code a rather rude surprise. You have no idea how it is using it. Maybe it wants to save before making major changes, oops. You implicitly hand out ownership of the object to the external code, it is up to that code to dispose it. – Hans Passant May 19 '16 at 10:30

1 Answers1

2

First off all, you seem to be misunderstanding the concept of managed and unmanaged resources. wordDoc is not an unmanaged resource, its a managed resource which happens to either directly hold an unmanaged resource or acts as a wrapper around some other IDisposable object (you don't care wich of the two is true). Its important you are clear on this because otherwise you will not implement correctly the IDisposable pattern when you need to. Read this for a very instructive answer on the subject and a few chuckles courtesy of Eric Lippert.

Does the following implementation do what I intend ?

No, it does not and to make things worse, the contract of DocXManager is simply horrible (more on that later).

What hapens if wordDoc.Save() throws and exception because the file is being used by another process, or maybe you've run out of space in the hard drive, or you've lost connection, etc.?

If the thrown exception is not recoverable (it's not handled anywhere in your code or it is but you will terminate it ASAP) then it's not really an issue and the runtime will clean up everything behind you when the process is terminated. On the other hand, if the exception is handled (warn the user the file is in use, the directory is not available, etc.) and the process keeps running then you've just maybe leaked a resource.

How to avoid this? Use a try-finally block:

public void makeChangesAndSaveDocX(DocX wordDoc)
{
   try
   {
       wordDoc.Save();
   }
   finally
   {
      ((IDisposable)wordDoc).Dispose();
   }
} 

Now you are sure that Dispose() will be called in any recoverable scenario.

So, is this good enough? Well....not exactly. The problem here is that makeChangesAndSaveDocX's (that should be MakeChangesAndSaveDocX by the way) contract is unclear. Who is responsible of disposing wordDoc? MakeChangesAndSaveDocX or the caller? Why one or the other? How does the consumer know that he doesn't need to worry about wordDoc once he's called MakeChangesAndSaveDocX? Or how is he supposed to know he can't use wordDoc after calling the public method MakeChangesAndSaveDocX? Why does DocXManager assume that the consumer will not need to use wordDoc after calling MakeChangesAndSaveDocX? Yuck...this is a mess.

I'd recommend you reconsider your approach and do one of the following:

  1. If the method signature MakeChangesAndSaveDocX(DocX wordDoc) makes sense (somebody else can own wordDoc), then do not dispose wordDoc in MakeChangesAndSaveDocX. Let the caller carry that burden, he should be responsible because the object belongs to him not to MakeChangesAndSaveDocX.
  2. If on the other hand, it makes no sense that somebody else who is not DocXManager owns wordDoc then wordDoc should be part of the state of DocXManager and you should reconsider the implementation of DocXManager to something allong the following lines:

     public class DocXManager: IDisposable
     {
         private readonly DocX docX;
         private bool disposed;
    
         public DocXManager(string filePath)
         {
             docX = new DocX(filePath);
         }
    
         public void MakeChangesAndSaveDocX()
         {
             if (disposed) 
                throw new ObjectDisposedException();
    
             docX.Save();
         }
    
         public void FrobDocX(Frobber frobber)
         {
             if (disposed) 
                 throw new ObjectDisposedException();
    
             frobber.Frob(docX);
         }
    
         public void Dispose()
         {
             if (disposed)
                return;
    
             Dispose(true);
             disposed = true;
             GC.SupressFinalize(this);   
         }
    
         public void Dispose(bool disposing)
         {
             if (disposing)
             {
                 //we can sefely dispose managed resources here
                 ((IDisposable)docX).Dispose();
             }
    
             //unsafe to clean up managed resources here, only clean up unmanaged resources if any.
         }
    
         ~DocXManager()
         {
             Dispose(false);
         }
    } 
    

Now you've got a clear contract; DocManagerX is responsible of disposing correctly of DocX and the consumer is responsible of disposing correctly of any instance of DocManagerX he might use. Once responsibilities are clear, its easier to reason about code correctness and who should do what.

You'd use the manager in the following way:

using (var manager = new DocXManager(path))
{
     manager.FrobDoxX(frobber);
     manager.MakeChangesAndSaveDocX();
} //manager is guaranteed to be disposed at this point (ignoring scenarios where finally blocks are not executed; StackOverflowException, OutOfMemoryException, etc.)
Community
  • 1
  • 1
InBetween
  • 32,319
  • 3
  • 50
  • 90
  • Just a note. In general, a safer way is to (docX as IDisposable)?.Dispose(); the object. docX becomes NULL if it doesn't implement IDisposable. "?Dispose()" is executed if, and only if, the object in front is something other than NULL. But in this case its okay i suppose. docX is known to always implement IDispose. – Frode May 19 '16 at 10:20
  • 1
    @Frode, I don't understand how anything can be "safer" by hiding an obvious bug in your program where the object unintentially becomes NULL or doesn't implement IDisposable? – adrianm May 19 '16 at 10:51
  • @Frode Why would I create an `IDisposable` wrapper around `docX` if it didn't implement `IDisposable` to begin with? The whole point of how the class is written is that I *know* beforehand that `docX` is disposable. I have to agree with [adrianm](http://stackoverflow.com/users/157224/adrianm) on this one; if `docX as IDisposable` ever returns `null` I have a bug in my code and the sooner I know about it the better. – InBetween May 19 '16 at 12:46
  • I was too quick to comment. I meant "robust" not "safe" (pardon my english). Comments on null-checking is probably more appropriate in other use-cases with polymorphisnm and mixed use of interfaces or where lazy-initialisation is used. I tend to prefer "obj as IDisposable" instead of "(IDisposable)obj" and the new "?" syntax for null checking in those cases. Not the case in this question. This question was related to a known, and always present, interface. – Frode May 19 '16 at 18:15