SingleAssignmentDisposable.cs 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the Apache 2.0 License.
  3. // See the LICENSE file in the project root for more information.
  4. using System.Threading;
  5. namespace System.Reactive.Disposables
  6. {
  7. /// <summary>
  8. /// Represents a disposable resource which only allows a single assignment of its underlying disposable resource.
  9. /// If an underlying disposable resource has already been set, future attempts to set the underlying disposable resource will throw an <see cref="T:System.InvalidOperationException"/>.
  10. /// </summary>
  11. public sealed class SingleAssignmentDisposable : ICancelable
  12. {
  13. private volatile IDisposable _current;
  14. /// <summary>
  15. /// Initializes a new instance of the <see cref="T:System.Reactive.Disposables.SingleAssignmentDisposable"/> class.
  16. /// </summary>
  17. public SingleAssignmentDisposable()
  18. {
  19. }
  20. /// <summary>
  21. /// Gets a value that indicates whether the object is disposed.
  22. /// </summary>
  23. public bool IsDisposed
  24. {
  25. get
  26. {
  27. // We use a sentinel value to indicate we've been disposed. This sentinel never leaks
  28. // to the outside world (see the Disposable property getter), so no-one can ever assign
  29. // this value to us manually.
  30. return _current == BooleanDisposable.True;
  31. }
  32. }
  33. /// <summary>
  34. /// Gets or sets the underlying disposable. After disposal, the result of getting this property is undefined.
  35. /// </summary>
  36. /// <exception cref="InvalidOperationException">Thrown if the SingleAssignmentDisposable has already been assigned to.</exception>
  37. public IDisposable Disposable
  38. {
  39. get
  40. {
  41. var current = _current;
  42. if (current == BooleanDisposable.True)
  43. return DefaultDisposable.Instance; // Don't leak the sentinel value.
  44. return current;
  45. }
  46. set
  47. {
  48. #pragma warning disable 0420
  49. var old = Interlocked.CompareExchange(ref _current, value, null);
  50. #pragma warning restore 0420
  51. if (old == null)
  52. return;
  53. if (old != BooleanDisposable.True)
  54. throw new InvalidOperationException(Strings_Core.DISPOSABLE_ALREADY_ASSIGNED);
  55. if (value != null)
  56. value.Dispose();
  57. }
  58. }
  59. /// <summary>
  60. /// Disposes the underlying disposable.
  61. /// </summary>
  62. public void Dispose()
  63. {
  64. #pragma warning disable 0420
  65. var old = Interlocked.Exchange(ref _current, BooleanDisposable.True);
  66. #pragma warning restore 0420
  67. if (old != null)
  68. old.Dispose();
  69. }
  70. }
  71. }