Dispose analysis
To reduce the number of resource leaks in your code and improve its performance, you need to ensure the correct handling of disposable resources. On the one hand, you want to enforce the using
keyword at call sites of the specific APIs, but on the other hand, you don't want to have a lot of noise with false positives around each usage of IDisposable
.
Therefore, to analyze the handling of disposable resources, JetBrains Rider relies on a set of annotation attributes from JetBrains.Annotations.
To start the analysis, annotate your critical disposable APIs with the [MustDisposeResource] attribute. You can use this attribute to annotate disposable types, their constructors, and factory methods. Once this is done, JetBrains Rider will report call sites that are not treating the resource as disposable and suggest the corresponding quick-fixes:
![JetBrains Rider: Dispose analysis. Enforce 'using' directive JetBrains Rider: Dispose analysis. Enforce 'using' directive](https://resources.jetbrains.com/help/img/rider/2024.3/dispose_analysis_1.png)
The warning will disappear as soon as you wrap the disposable resource in a using
or explicitly call Dispose()
on that resource.
As you can see on the screenshot above, usages of the [MustDisposeResource]
are also marked with the corresponding inlay hints, which are configurable on the Editor | Inlay Hints | C# | Other page of JetBrains Rider settings CtrlAlt0S.
If your API does not expose the Dispose()
method or have several methods that handle the disposal, you can annotate those methods that actually dispose resources with the [HandlesResourceDisposal] attribute:
[MustDisposeResource]
public class HasNativeResources
{
// acquire disposable resources
[HandlesResourceDisposal]
public void Close() {/* process disposal */}
}
public class Test
{
public Test()
{
var resource = new HasNativeResources();
// no warning, because `Close()` handles the disposal
resource.Close();
}
}
If a custom dispose method expects a disposable resource as an argument, you can annotate the corresponding parameter with [HandlesResourceDisposal]
:
[MustDisposeResource]
public class HasNativeResources : IDisposable
{
// some work with native resources
public void Dispose() {/* process disposal */}
}
public static class ResourceHelpers
{
public static void Close([HandlesResourceDisposal] HasNativeResources res)
{
// handle disposal
}
}
public class Test
{
public Test()
{
// no warning, because `Close()` handles the disposal
var resource = new HasNativeResources();
ResourceHelpers.Close(resource);
}
}
Finally, if a method that obtains a resource from an annotated source does not handle resource disposal, but returns it to other callers, the problem of potential incorrect handling of the resource is passed to the callers. If this is deliberate, you can let the analyzer know that by annotating the method with [MustDisposeResource]
to explicitly delegate the responsibility of handling the disposable resource to the callers:
[MustDisposeResource]
public class HasNativeResources : IDisposable
{
// some work with native resources
public void Dispose() {/* process disposal */}
}
public class Sample
{
[MustDisposeResource]
public HasNativeResources ProcessWithoutDisposing()
{
var resource = new HasNativeResources();
Console.WriteLine($"Logging resource: {resource}");
// no warning because the method is marked with [MustDisposeResource]
return resource;
}
}
note
If you do not have access to the sources of the disposable API, you can use external annotations to apply the necessary attributes to symbols in a compiled assembly.
When you receive an object via a [MustDisposeResource] API, JetBrains Rider will always consider it disposed and issue no warnings if you wrap the corresponding variable in a using
or if you explicitly call Dispose()
on that variable.
If instead you pass the disposable variable as an argument to a method, JetBrains Rider will issue warnings depending on the analysis mode:
Optimistic (default): All methods that accept
IDisposable
or a derived type are considered safe; that is we assume that they will callDispose()
after processing the object.Pessimistic: A method is only considered safe if the parameter that accepts
IDisposable
is marked with the [HandlesResourceDisposal] attribute. Otherwise, JetBrains Rider will report a non-disposed variable.
You can change the analysis mode on the Editor | Inspection Settings page of JetBrains Rider settings CtrlAlt0S.
The dispose analysis relies on the following code inspections:
Code inspection: Return value of a method annotated with [MustDisposeResource] is never disposed
Code inspection: Return value of a property must be disposed by the callee
Make sure that these inspections are enabled on the Editor | Inspection Settings | Inspection Severity page of JetBrains Rider settings CtrlAlt0S.
To fine-tune the analysis, you can change severity levels of specific inspections or suppress some inspections in places that should be ignored.