Exception handling in IDisposable.Dispose

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Joe

    Exception handling in IDisposable.Dispose

    Reposting here as there were no useful replies in the dotnet.framewor k NG...

    What is the correct pattern for handling exceptions in IDisposable.Dis pose,
    especially in a class that manages multiple unmanaged resources? An example
    of such a class is System.Componen tModel.Containe r.

    I have always understood that the IDisposable contract was that Dispose
    guarantees to release unmanaged resources owned by the object - even if the
    Dispose method throws an exception.

    This seems to be underlined by the .NET 2.0 implementation of StreamWriter.

    The StreamWriter Dispose method attempts to flush the stream before closing
    it: this can of course throw an exception (e.g. the stream may represent a
    network file).

    In .NET 1.1, an exception thrown while Dispose is flushing the stream is
    propagated to the caller and the stream is not closed. The unmanaged
    resource is not released.

    This has been fixed in .NET 2.0: a try/finally block is used to ensure the
    stream is always closed.

    The fact that this was fixed seems to imply that Microsoft considers the
    correct behavior is to release unmanaged resources whether or not there is an
    exception.

    What if a class, like System.Componen tModel.Containe r contains multiple
    unmanaged resources?

    Using Reflector on System.Componen tModel.Containe r shows it contains code
    something like the following:

    ....
    while (siteCount > 0)
    {
    ISite site = sites[siteCount-1];
    site.Component. Site = null;
    site.Component. Dispose();
    }
    ....

    This means if one of the components has a Dispose method which throws an
    exception, then Dispose will not be called on the remaining components.
    Which appears to break the IDispose.Dispos e contract.

    The only way I can see to guarantee that all resources are disposed would be
    to use try/catch blocks - which signficiantly complicates the Dispose
    pattern. In the case of Container, it could be something like:

    ....
    Exception savedException = null;
    while (siteCount > 0)
    {
    try
    {
    ISite site = sites[siteCount-1];
    site.Component. Site = null;
    site.Component. Dispose();
    }
    catch(Exception ex)
    {
    if (savedException == null) savedException = ex;
    }
    }
    if (savedException == null)
    {
    // Wrap savedException in a new exception and throw it.
    // Or better, save all exceptions above in a collection and add
    // the collection to the new custom exception.
    ...
    }
    ....




    Expand AllCollapse All

Working...