Exception In Dispose
ID |
csharp.exception_in_dispose |
Severity |
high |
Remediation Complexity |
trivial |
Remediation Risk |
low |
Remediation Effort |
low |
Resource |
Reliability |
Language |
CSharp |
Tags |
dispose, reliability |
Description
Reports Dispose() or Dispose(bool) methods that contain a throw statement. Dispose
is the runtime’s standard mechanism for releasing resources, called by using statements
and by finally blocks during cleanup; throwing from it masks the original exception and
leaks unreleased resources.
Rationale
When an exception is already being propagated and a finally (including the implicit one
generated by using) calls Dispose, a throw from Dispose replaces the original
exception. The information about what really went wrong is lost, additional cleanup that
should still happen is skipped, and code that legitimately relies on Dispose being safe
to call (idempotent, non-throwing) is left in an undefined state.
The accepted convention is that Dispose must be tolerant: it should log or swallow
unexpected conditions, never propagate them.
public class Connection : IDisposable
{
public void Dispose()
{
if (state != Closed)
throw new InvalidOperationException("not closed"); // FLAW
}
}
public class ConnectionTemplate : IDisposable
{
protected virtual void Dispose(bool disposing)
{
if (disposing && state != Closed)
throw new InvalidOperationException("not closed"); // FLAW
}
public void Dispose() => Dispose(true);
}
public class ConnectionOk : IDisposable
{
public void Dispose() // OK
{
try { Close(); } catch (Exception ex) { Log(ex); }
}
}