Selaa lähdekoodia

Add an AnonymousDisposable that can be given a state object. Add a generic Disposable.Create method. (#634)

Daniel C. Weber 7 vuotta sitten
vanhempi
sitoutus
927fac9d6c

+ 36 - 0
Rx.NET/Source/src/System.Reactive/Disposables/AnonymousDisposable.cs

@@ -37,4 +37,40 @@ namespace System.Reactive.Disposables
             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 - 0
Rx.NET/Source/src/System.Reactive/Disposables/Disposable.cs

@@ -60,6 +60,21 @@ namespace System.Reactive.Disposables
             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.

+ 1 - 0
Rx.NET/Source/tests/Tests.System.Reactive.ApiApprovals/Api/ApiApprovalTests.Core.approved.txt

@@ -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
     {