// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the Apache 2.0 License. // See the LICENSE file in the project root for more information. namespace System.Reactive.Disposables { /// /// Represents a disposable resource whose underlying disposable resource can be swapped for another disposable resource. /// public sealed class MultipleAssignmentDisposable : ICancelable { private readonly object _gate = new object(); private IDisposable _current; /// /// Initializes a new instance of the class with no current underlying disposable. /// public MultipleAssignmentDisposable() { } /// /// Gets a value that indicates whether the object is disposed. /// public bool IsDisposed { get { lock (_gate) { // We use a sentinel value to indicate we've been disposed. This sentinel never leaks // to the outside world (see the Disposable property getter), so no-one can ever assign // this value to us manually. return _current == BooleanDisposable.True; } } } /// /// Gets or sets the underlying disposable. After disposal, the result of getting this property is undefined. /// /// If the MutableDisposable has already been disposed, assignment to this property causes immediate disposal of the given disposable object. public IDisposable Disposable { get { lock (_gate) { if (_current == BooleanDisposable.True /* see IsDisposed */) return DefaultDisposable.Instance; // Don't leak the sentinel value. return _current; } } set { var shouldDispose = false; lock (_gate) { shouldDispose = (_current == BooleanDisposable.True /* see IsDisposed */); if (!shouldDispose) { _current = value; } } if (shouldDispose && value != null) value.Dispose(); } } /// /// Disposes the underlying disposable as well as all future replacements. /// public void Dispose() { var old = default(IDisposable); lock (_gate) { if (_current != BooleanDisposable.True /* see IsDisposed */) { old = _current; _current = BooleanDisposable.True /* see IsDisposed */; } } if (old != null) old.Dispose(); } } }