-
Notifications
You must be signed in to change notification settings - Fork 237
Open
Description
Description
The rule requires that all fields implementing the IDisposable interface be disposed in the parent's Dispose() method.
The rule should not be triggered if the field is disposed AND in both the method AND the Dispose() function of the containing class.
This is because a class can create multiple IDisposable instances during its lifecycle, store them in the same field, and free them multiple times when replacing them. Calling the Dispose() method early promotes more efficient memory cleanup than passively losing the reference.
Reproducer
internal sealed class Foo: IDisposable
{
// Any IDisposable field
private CancellationTokenSource _disposableMember;
private CancellationToken _processToken;
private readonly SemaphoreSlim _processSync;
private bool _isDisposed;
private int MagicNumber { get; set; }
public bool IsWorkComplete { get; set; }
// I cannot annotate this constructor which instantiate Process through property in .NET Framework 4.7.2
public Foo()
{
_processSync = new SemaphoreSlim(1, 1);
_disposableMember = new CancellationTokenSource();
_processToken = _disposableMember.Token;
IsWorkComplete = false;
MagicNumber = -1;
_isDisposed = false;
}
private Task<int> Process
{
get;
set
{
// Dispose old instance at once
// HERE WARNING
_disposableMember.Dispose();
// Recreate instance
_disposableMember = new CancellationTokenSource();
_processToken = _disposableMember.Token;
field = value;
}
}
public Task<int> GetHardWorkProcessAsync(CancellationToken token = default) // Intentionally synchronous here
{
// Attach token
token.Register(_disposableMember.Cancel);
return StartHardWorkAsync(token);
}
public int Bar(int defaultMagicNumber = -1)
{
if (IsWorkComplete)
return MagicNumber;
return defaultMagicNumber;
}
public async Task<int> BarAsync(int defaultMagicNumber = -1, CancellationToken token = default)
{
await _processSync .WaitAsync(token);
try
{
if (IsWorkComplete)
return MagicNumber;
token.Register(_disposableMember.Cancel);
MagicNumber = await Process.ConfigureAwait(false);
IsWorkComplete = true;
return MagicNumber;
}
catch (Exception)
{
// Intentional exception suppression here
// You can catch the process exception by awaiting the task from GetHardWorkProcessAsync()
Process = StartHardWorkAsync(_processToken);
IsWorkComplete = false;
return defaultMagicNumber;
}
finally
{
_processSync.Release();
}
}
private static async Task<int> StartHardWorkAsync(CancellationToken token = default)
{
await Task.Delay(1000, token);
return 42;
}
public void Dispose()
{
if (_isDisposed)
return;
_disposableMember.Dispose(); // The member is Disposed!
_processSync.Dispose();
_isDisposed = true;
}
}Product and Version
SonarQube for IDE (Jetbrains Rider) v11.9.0.83922
Metadata
Metadata
Assignees
Labels
No labels