SingleAssignmentDisposable.cs 2.8 KB

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