157 lines
4.3 KiB
Plaintext
157 lines
4.3 KiB
Plaintext
== Why is this an issue?
|
|
|
|
The `IDisposable` interface is a mechanism to release unmanaged resources, if not implemented correctly this could result in resource leaks or more severe bugs.
|
|
|
|
|
|
This rule raises an issue when the recommended dispose pattern, as defined by Microsoft, is not adhered to. See the *Compliant Solution* section for examples.
|
|
|
|
|
|
Satisfying the rule's conditions will enable potential derived classes to correctly dispose the members of your class:
|
|
|
|
* `sealed` classes are not checked.
|
|
* If a base class implements `IDisposable` your class should not have `IDisposable` in the list of its interfaces. In such cases it is recommended to override the base class's `protected virtual void Dispose(bool)` method or its equivalent.
|
|
* The class should not implement `IDisposable` explicitly, e.g. the `Dispose()` method should be public.
|
|
* The class should contain `protected virtual void Dispose(bool)` method. This method allows the derived classes to correctly dispose the resources of this class.
|
|
* The content of the `Dispose()` method should be invocation of `Dispose(true)` followed by `GC.SuppressFinalize(this)`
|
|
* If the class has a finalizer, i.e. a destructor, the only code in its body should be a single invocation of `Dispose(false)`.
|
|
* If the class inherits from a class that implements `IDisposable` it must call the `Dispose`, or `Dispose(bool)` method of the base class from within its own implementation of `Dispose` or `Dispose(bool)`, respectively. This ensures that all resources from the base class are properly released.
|
|
|
|
=== Noncompliant code example
|
|
|
|
[source,csharp]
|
|
----
|
|
public class Foo1 : IDisposable // Noncompliant - provide protected overridable implementation of Dispose(bool) on Foo or mark the type as sealed.
|
|
{
|
|
public void Dispose() // Noncompliant - should contain only a call to Dispose(true) and then GC.SuppressFinalize(this)
|
|
{
|
|
// Cleanup
|
|
}
|
|
}
|
|
|
|
public class Foo2 : IDisposable
|
|
{
|
|
void IDisposable.Dispose() // Noncompliant - Dispose() should be public
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
public virtual void Dispose() // Noncompliant - Dispose() should be sealed
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
}
|
|
|
|
public class Foo3 : IDisposable
|
|
{
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
// Cleanup
|
|
}
|
|
|
|
~Foo3() // Noncompliant - Modify Foo.~Foo() so that it calls Dispose(false) and then returns.
|
|
{
|
|
// Cleanup
|
|
}
|
|
}
|
|
----
|
|
|
|
=== Compliant solution
|
|
|
|
[source,csharp]
|
|
----
|
|
// Sealed class
|
|
public sealed class Foo1 : IDisposable
|
|
{
|
|
public void Dispose()
|
|
{
|
|
// Cleanup
|
|
}
|
|
}
|
|
|
|
// Simple implementation
|
|
public class Foo2 : IDisposable
|
|
{
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
// Cleanup
|
|
}
|
|
}
|
|
|
|
// Implementation with a finalizer
|
|
public class Foo3 : IDisposable
|
|
{
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
// Cleanup
|
|
}
|
|
|
|
~Foo3()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
}
|
|
|
|
// Base disposable class
|
|
public class Foo4 : DisposableBase
|
|
{
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
// Cleanup
|
|
// Do not forget to call base
|
|
base.Dispose(disposing);
|
|
}
|
|
}
|
|
----
|
|
|
|
|
|
== Resources
|
|
|
|
=== Documentation
|
|
|
|
* https://msdn.microsoft.com/en-us/library/498928w2.aspx[MSDN] for complete documentation on the dispose pattern.
|
|
* https://blog.stephencleary.com/2009/08/how-to-implement-idisposable-and.html[Stephen Cleary] for excellent Q&A about IDisposable
|
|
* https://pragmateek.com/c-scope-your-global-state-changes-with-idisposable-and-the-using-statement/[Pragma Geek] for additional usages of IDisposable, beyond releasing resources.
|
|
* https://docs.microsoft.com/en-us/dotnet/api/system.idisposable?view=netframework-4.7[IDisposable documentation]
|
|
|
|
|
|
ifdef::env-github,rspecator-view[]
|
|
|
|
'''
|
|
== Implementation Specification
|
|
(visible only on this page)
|
|
|
|
=== Message
|
|
|
|
Fix this implementation of `IDisposable` to conform to the dispose pattern.
|
|
|
|
|
|
'''
|
|
== Comments And Links
|
|
(visible only on this page)
|
|
|
|
=== relates to: S4002
|
|
|
|
=== is related to: S3971
|
|
|
|
endif::env-github,rspecator-view[]
|