// 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();
}
}
}