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
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