Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 110 additions & 1 deletion csharp/Platform.Disposables.Tests/DisposableTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ private static ProcessStartInfo CreateProcessStartInfo(string logPath, bool wait
return new ProcessStartInfo
{
FileName = "dotnet",
Arguments = $"run -p \"{projectPath}\" -f net7 \"{logPath}\" {waitForCancellation.ToString()}",
Arguments = $"run -p \"{projectPath}\" -f net8 \"{logPath}\" {waitForCancellation.ToString()}",
UseShellExecute = false,
CreateNoWindow = true
};
Expand Down Expand Up @@ -79,5 +79,114 @@ private static string GetDisposalObjectTestProjectFilePath()
}
return path;
}

[Fact]
public static void AllowMultipleDisposeCallsConstructorTest()
{
var disposeCount = 0;
Action incrementCount = () => disposeCount++;

var disposable = new Disposable(incrementCount, allowMultipleDisposeCalls: true);

disposable.Dispose();
disposable.Dispose();
disposable.Dispose();

Assert.Equal(1, disposeCount);
}

[Fact]
public static void AllowMultipleDisposeAttemptsConstructorTest()
{
var disposeCount = 0;
Disposal incrementCount = (manual, wasDisposed) =>
{
if (!wasDisposed) disposeCount++;
};

var disposable = new Disposable(incrementCount, allowMultipleDisposeCalls: true, allowMultipleDisposeAttempts: true);

disposable.Dispose();
disposable.Dispose();

Assert.Equal(1, disposeCount);
}

[Fact]
public static void ParameterlessConstructorWithAllowMultipleDisposeCallsTest()
{
var disposable = new Disposable(allowMultipleDisposeCalls: true);

disposable.Dispose();
disposable.Dispose();
}

[Fact]
public static void ParameterlessConstructorWithAllowMultipleDisposeAttemptsTest()
{
var disposable = new Disposable(allowMultipleDisposeCalls: true, allowMultipleDisposeAttempts: true);

disposable.Dispose();
disposable.Dispose();
}

[Fact]
public static void DefaultParameterlessConstructorTest()
{
var disposable = new Disposable();

disposable.Dispose();

Assert.Throws<ObjectDisposedException>(() => disposable.Dispose());
}

[Fact]
public static void DefaultActionConstructorTest()
{
var disposeCount = 0;
Action incrementCount = () => disposeCount++;

var disposable = new Disposable(incrementCount);

disposable.Dispose();

Assert.Equal(1, disposeCount);
Assert.Throws<ObjectDisposedException>(() => disposable.Dispose());
}

[Fact]
public static void DefaultDisposalConstructorTest()
{
var disposeCount = 0;
Disposal incrementCount = (manual, wasDisposed) =>
{
if (!wasDisposed) disposeCount++;
};

var disposable = new Disposable(incrementCount);

disposable.Dispose();

Assert.Equal(1, disposeCount);
Assert.Throws<ObjectDisposedException>(() => disposable.Dispose());
}

[Fact]
public static void ReplacesWorkaroundFromIssueTest()
{
var disposeCount = 0;
Disposal incrementCount = (manual, wasDisposed) =>
{
if (!wasDisposed) disposeCount++;
};

var disposableWithNewConstructor = new Disposable(incrementCount, allowMultipleDisposeCalls: true);

disposableWithNewConstructor.Dispose();
disposableWithNewConstructor.Dispose();
disposableWithNewConstructor.Dispose();

Assert.Equal(1, disposeCount);
}
}
}
52 changes: 50 additions & 2 deletions csharp/Platform.Disposables/Disposable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,47 @@ public class Disposable : DisposableBase
{
private static readonly Disposal _emptyDelegate = (manual, wasDisposed) => { };

private readonly bool? _allowMultipleDisposeCalls;
private readonly bool? _allowMultipleDisposeAttempts;

/// <summary>
/// <para>Occurs when the object is being disposed.</para>
/// <para>Возникает, когда объект высвобождается.</para>
/// </summary>
public event Disposal OnDispose;

/// <summary>
/// <para>Gets a value indicating whether multiple attempts to dispose this object are allowed.</para>
/// <para>Возвращает значение определяющие разрешено ли выполнять несколько попыток высвободить этот объект.</para>
/// </summary>
protected override bool AllowMultipleDisposeAttempts
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _allowMultipleDisposeAttempts ?? base.AllowMultipleDisposeAttempts;
}

/// <summary>
/// <para>Gets a value indicating whether it is allowed to call this object disposal multiple times.</para>
/// <para>Возвращает значение определяющие разрешено ли несколько раз вызывать высвобождение этого объекта.</para>
/// </summary>
protected override bool AllowMultipleDisposeCalls
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _allowMultipleDisposeCalls ?? base.AllowMultipleDisposeCalls;
}

/// <summary>
/// <para>Initializes a new instance of the <see cref="Disposable"/> object.</para>
/// <para>Инициализирует новый экземпляр объекта <see cref="Disposable"/>.</para>
/// </summary>
/// <param name="action"><para>The <see cref="Action"/> delegate.</para><para>Делегат <see cref="Action"/>.</para></param>
/// <param name="allowMultipleDisposeCalls"><para>A value indicating whether it is allowed to call this object disposal multiple times.</para><para>Значение, определяющее разрешено ли несколько раз вызывать высвобождение этого объекта.</para></param>
/// <param name="allowMultipleDisposeAttempts"><para>A value indicating whether multiple attempts to dispose this object are allowed.</para><para>Значение, определяющее разрешено ли выполнять несколько попыток высвободить этот объект.</para></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Disposable(Action action)
public Disposable(Action action, bool allowMultipleDisposeCalls = false, bool allowMultipleDisposeAttempts = false)
{
_allowMultipleDisposeCalls = allowMultipleDisposeCalls;
_allowMultipleDisposeAttempts = allowMultipleDisposeAttempts;
OnDispose = (manual, wasDisposed) =>
{
if (!wasDisposed)
Expand All @@ -39,8 +66,15 @@ public Disposable(Action action)
/// <para>Инициализирует новый экземпляр объекта <see cref="Disposable"/>.</para>
/// </summary>
/// <param name="disposal"><para>The <see cref="Disposal"/> delegate.</para><para>Делегат <see cref="Disposal"/>.</para></param>
/// <param name="allowMultipleDisposeCalls"><para>A value indicating whether it is allowed to call this object disposal multiple times.</para><para>Значение, определяющее разрешено ли несколько раз вызывать высвобождение этого объекта.</para></param>
/// <param name="allowMultipleDisposeAttempts"><para>A value indicating whether multiple attempts to dispose this object are allowed.</para><para>Значение, определяющее разрешено ли выполнять несколько попыток высвободить этот объект.</para></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Disposable(Disposal disposal) => OnDispose = disposal;
public Disposable(Disposal disposal, bool allowMultipleDisposeCalls = false, bool allowMultipleDisposeAttempts = false)
{
_allowMultipleDisposeCalls = allowMultipleDisposeCalls;
_allowMultipleDisposeAttempts = allowMultipleDisposeAttempts;
OnDispose = disposal;
}

/// <summary>
/// <para>Initializes a new instance of the <see cref="Disposable"/> object.</para>
Expand All @@ -49,6 +83,20 @@ public Disposable(Action action)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Disposable() => OnDispose = _emptyDelegate;

/// <summary>
/// <para>Initializes a new instance of the <see cref="Disposable"/> object.</para>
/// <para>Инициализирует новый экземпляр объекта <see cref="Disposable"/>.</para>
/// </summary>
/// <param name="allowMultipleDisposeCalls"><para>A value indicating whether it is allowed to call this object disposal multiple times.</para><para>Значение, определяющее разрешено ли несколько раз вызывать высвобождение этого объекта.</para></param>
/// <param name="allowMultipleDisposeAttempts"><para>A value indicating whether multiple attempts to dispose this object are allowed.</para><para>Значение, определяющее разрешено ли выполнять несколько попыток высвободить этот объект.</para></param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Disposable(bool allowMultipleDisposeCalls, bool allowMultipleDisposeAttempts = false)
{
_allowMultipleDisposeCalls = allowMultipleDisposeCalls;
_allowMultipleDisposeAttempts = allowMultipleDisposeAttempts;
OnDispose = _emptyDelegate;
}

/// <summary>
/// <para>Creates a new <see cref="Disposable"/> object initialized with specified delegate <see cref="Action"/>.</para>
/// <para>Создает новый объект <see cref="Disposable"/>, инициализированную с помощью указанного делегата <see cref="Action"/>.</para>
Expand Down
Loading