Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,40 @@ public void Dispose()
Interlocked.Exchange(ref _dispose, null)?.Invoke();
}
}

/// <summary>
/// Represents a Action-based disposable that can hold onto some state.
/// </summary>
internal sealed class AnonymousDisposable<TState> : ICancelable
{
private TState _state;
private volatile Action<TState> _dispose;

/// <summary>
/// Constructs a new disposable with the given action used for disposal.
/// </summary>
/// <param name="state">The state to be passed to the disposal action.</param>
/// <param name="dispose">Disposal action which will be run upon calling Dispose.</param>
public AnonymousDisposable(TState state, Action<TState> dispose)
{
System.Diagnostics.Debug.Assert(dispose != null);

_state = state;
_dispose = dispose;
}

/// <summary>
/// Gets a value that indicates whether the object is disposed.
/// </summary>
public bool IsDisposed => _dispose == null;

/// <summary>
/// Calls the disposal action if and only if the current instance hasn't been disposed yet.
/// </summary>
public void Dispose()
{
Interlocked.Exchange(ref _dispose, null)?.Invoke(_state);
_state = default;
}
}
}
15 changes: 15 additions & 0 deletions Rx.NET/Source/src/System.Reactive/Disposables/Disposable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ public static IDisposable Create(Action dispose)
return new AnonymousDisposable(dispose);
}

/// <summary>
/// Creates a disposable object that invokes the specified action when disposed.
/// </summary>
/// <param name="state">The state to be passed to the action.</param>
/// <param name="dispose">Action to run during the first call to <see cref="IDisposable.Dispose"/>. The action is guaranteed to be run at most once.</param>
/// <returns>The disposable object that runs the given action upon disposal.</returns>
/// <exception cref="ArgumentNullException"><paramref name="dispose"/> is <c>null</c>.</exception>
public static IDisposable Create<TState>(TState state, Action<TState> dispose)
{
if (dispose == null)
throw new ArgumentNullException(nameof(dispose));

return new AnonymousDisposable<TState>(state, dispose);
}

/// <summary>
/// Gets the value stored in <paramref name="fieldRef" /> or a null if
/// <paramref name="fieldRef" /> was already disposed.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -568,6 +568,7 @@ namespace System.Reactive.Disposables
{
public static System.IDisposable Empty { get; }
public static System.IDisposable Create(System.Action dispose) { }
public static System.IDisposable Create<TState>(TState state, System.Action<TState> dispose) { }
}
public interface ICancelable : System.IDisposable
{
Expand Down