|
@@ -2,6 +2,7 @@
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
using System.Reactive.Concurrency;
|
|
|
+using System.Reactive.Disposables;
|
|
|
using System.Threading;
|
|
|
|
|
|
namespace System.Reactive.Subjects
|
|
@@ -11,14 +12,14 @@ namespace System.Reactive.Subjects
|
|
|
/// Each notification is broadcasted to all subscribed and future observers, subject to buffer trimming policies.
|
|
|
/// </summary>
|
|
|
/// <typeparam name="T">The type of the elements processed by the subject.</typeparam>
|
|
|
- public sealed class ReplaySubject<T> : ISubject<T>, IDisposable
|
|
|
+ public sealed class ReplaySubject<T> : SubjectBase<T>, IDisposable
|
|
|
{
|
|
|
#region Fields
|
|
|
|
|
|
/// <summary>
|
|
|
/// Underlying optimized implementation of the replay subject.
|
|
|
/// </summary>
|
|
|
- private readonly IReplaySubjectImplementation _implementation;
|
|
|
+ private readonly SubjectBase<T> _implementation;
|
|
|
|
|
|
#endregion
|
|
|
|
|
@@ -144,22 +145,30 @@ namespace System.Reactive.Subjects
|
|
|
/// <summary>
|
|
|
/// Indicates whether the subject has observers subscribed to it.
|
|
|
/// </summary>
|
|
|
- public bool HasObservers
|
|
|
+ public override bool HasObservers
|
|
|
{
|
|
|
get { return _implementation.HasObservers; }
|
|
|
}
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Indicates whether the subject has been disposed.
|
|
|
+ /// </summary>
|
|
|
+ public override bool IsDisposed
|
|
|
+ {
|
|
|
+ get { return _implementation.IsDisposed; }
|
|
|
+ }
|
|
|
+
|
|
|
#endregion
|
|
|
|
|
|
#region Methods
|
|
|
|
|
|
- #region Observer implementation
|
|
|
+ #region IObserver<T> implementation
|
|
|
|
|
|
/// <summary>
|
|
|
/// Notifies all subscribed and future observers about the arrival of the specified element in the sequence.
|
|
|
/// </summary>
|
|
|
/// <param name="value">The value to send to all observers.</param>
|
|
|
- public void OnNext(T value)
|
|
|
+ public override void OnNext(T value)
|
|
|
{
|
|
|
_implementation.OnNext(value);
|
|
|
}
|
|
@@ -169,22 +178,25 @@ namespace System.Reactive.Subjects
|
|
|
/// </summary>
|
|
|
/// <param name="error">The exception to send to all observers.</param>
|
|
|
/// <exception cref="ArgumentNullException"><paramref name="error"/> is null.</exception>
|
|
|
- public void OnError(Exception error)
|
|
|
+ public override void OnError(Exception error)
|
|
|
{
|
|
|
+ if (error == null)
|
|
|
+ throw new ArgumentNullException("error");
|
|
|
+
|
|
|
_implementation.OnError(error);
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
/// Notifies all subscribed and future observers about the end of the sequence.
|
|
|
/// </summary>
|
|
|
- public void OnCompleted()
|
|
|
+ public override void OnCompleted()
|
|
|
{
|
|
|
_implementation.OnCompleted();
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
- #region IObservable implementation
|
|
|
+ #region IObservable<T> implementation
|
|
|
|
|
|
/// <summary>
|
|
|
/// Subscribes an observer to the subject.
|
|
@@ -192,8 +204,11 @@ namespace System.Reactive.Subjects
|
|
|
/// <param name="observer">Observer to subscribe to the subject.</param>
|
|
|
/// <returns>Disposable object that can be used to unsubscribe the observer from the subject.</returns>
|
|
|
/// <exception cref="ArgumentNullException"><paramref name="observer"/> is null.</exception>
|
|
|
- public IDisposable Subscribe(IObserver<T> observer)
|
|
|
+ public override IDisposable Subscribe(IObserver<T> observer)
|
|
|
{
|
|
|
+ if (observer == null)
|
|
|
+ throw new ArgumentNullException("observer");
|
|
|
+
|
|
|
return _implementation.Subscribe(observer);
|
|
|
}
|
|
|
|
|
@@ -204,7 +219,7 @@ namespace System.Reactive.Subjects
|
|
|
/// <summary>
|
|
|
/// Releases all resources used by the current instance of the <see cref="System.Reactive.Subjects.ReplaySubject<T>"/> class and unsubscribe all observers.
|
|
|
/// </summary>
|
|
|
- public void Dispose()
|
|
|
+ public override void Dispose()
|
|
|
{
|
|
|
_implementation.Dispose();
|
|
|
}
|
|
@@ -213,12 +228,7 @@ namespace System.Reactive.Subjects
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
- private interface IReplaySubjectImplementation : ISubject<T>, IDisposable
|
|
|
- {
|
|
|
- bool HasObservers { get; }
|
|
|
- }
|
|
|
-
|
|
|
- private abstract class ReplayBase : IReplaySubjectImplementation
|
|
|
+ private abstract class ReplayBase : SubjectBase<T>
|
|
|
{
|
|
|
private readonly object _gate = new object();
|
|
|
|
|
@@ -236,7 +246,7 @@ namespace System.Reactive.Subjects
|
|
|
_error = null;
|
|
|
}
|
|
|
|
|
|
- public bool HasObservers
|
|
|
+ public override bool HasObservers
|
|
|
{
|
|
|
get
|
|
|
{
|
|
@@ -245,7 +255,18 @@ namespace System.Reactive.Subjects
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public void OnNext(T value)
|
|
|
+ public override bool IsDisposed
|
|
|
+ {
|
|
|
+ get
|
|
|
+ {
|
|
|
+ lock (_gate)
|
|
|
+ {
|
|
|
+ return _isDisposed;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public override void OnNext(T value)
|
|
|
{
|
|
|
var o = default(IScheduledObserver<T>[]);
|
|
|
lock (_gate)
|
|
@@ -270,11 +291,8 @@ namespace System.Reactive.Subjects
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public void OnError(Exception error)
|
|
|
+ public override void OnError(Exception error)
|
|
|
{
|
|
|
- if (error == null)
|
|
|
- throw new ArgumentNullException("error");
|
|
|
-
|
|
|
var o = default(IScheduledObserver<T>[]);
|
|
|
lock (_gate)
|
|
|
{
|
|
@@ -301,7 +319,7 @@ namespace System.Reactive.Subjects
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public void OnCompleted()
|
|
|
+ public override void OnCompleted()
|
|
|
{
|
|
|
var o = default(IScheduledObserver<T>[]);
|
|
|
lock (_gate)
|
|
@@ -328,16 +346,14 @@ namespace System.Reactive.Subjects
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- public IDisposable Subscribe(IObserver<T> observer)
|
|
|
+ public override IDisposable Subscribe(IObserver<T> observer)
|
|
|
{
|
|
|
- if (observer == null)
|
|
|
- throw new ArgumentNullException("observer");
|
|
|
-
|
|
|
var so = CreateScheduledObserver(observer);
|
|
|
|
|
|
var n = 0;
|
|
|
|
|
|
- var subscription = new Subscription(this, so);
|
|
|
+ var subscription = Disposable.Empty;
|
|
|
+
|
|
|
lock (_gate)
|
|
|
{
|
|
|
CheckDisposed();
|
|
@@ -362,7 +378,6 @@ namespace System.Reactive.Subjects
|
|
|
// reasons with v1.x.
|
|
|
//
|
|
|
Trim();
|
|
|
- _observers = _observers.Add(so);
|
|
|
|
|
|
n = Replay(so);
|
|
|
|
|
@@ -376,6 +391,12 @@ namespace System.Reactive.Subjects
|
|
|
n++;
|
|
|
so.OnCompleted();
|
|
|
}
|
|
|
+
|
|
|
+ if (!_isStopped)
|
|
|
+ {
|
|
|
+ subscription = new Subscription(this, so);
|
|
|
+ _observers = _observers.Add(so);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
so.EnsureActive(n);
|
|
@@ -383,7 +404,7 @@ namespace System.Reactive.Subjects
|
|
|
return subscription;
|
|
|
}
|
|
|
|
|
|
- public void Dispose()
|
|
|
+ public override void Dispose()
|
|
|
{
|
|
|
lock (_gate)
|
|
|
{
|
|
@@ -420,6 +441,46 @@ namespace System.Reactive.Subjects
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+#if NOTYET // TODO: Expose internal notifications similar to BehaviorSubject<T>.TryGetValue?
|
|
|
+
|
|
|
+ public bool TryGetNotifications(out IList<Notification<T>> notifications)
|
|
|
+ {
|
|
|
+ lock (_gate)
|
|
|
+ {
|
|
|
+ if (_isDisposed)
|
|
|
+ {
|
|
|
+ notifications = null;
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ var res = new List<Notification<T>>();
|
|
|
+
|
|
|
+ var materializer = Observer.Create<T>(
|
|
|
+ x => res.Add(Notification.CreateOnNext(x)),
|
|
|
+ ex => res.Add(Notification.CreateOnError<T>(ex)),
|
|
|
+ () => res.Add(Notification.CreateOnCompleted<T>())
|
|
|
+ );
|
|
|
+
|
|
|
+ Replay(materializer);
|
|
|
+
|
|
|
+ if (_error != null)
|
|
|
+ {
|
|
|
+ materializer.OnError(_error);
|
|
|
+ }
|
|
|
+ else if (_isStopped)
|
|
|
+ {
|
|
|
+ materializer.OnCompleted();
|
|
|
+ }
|
|
|
+
|
|
|
+ notifications = res;
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+#endif
|
|
|
+
|
|
|
private sealed class Subscription : IDisposable
|
|
|
{
|
|
|
private readonly ReplayBase _subject;
|
|
@@ -539,7 +600,7 @@ namespace System.Reactive.Subjects
|
|
|
// The ReplayOne implementation also removes the need to even have a queue.
|
|
|
//
|
|
|
|
|
|
- private sealed class ReplayOne : ReplayBufferBase, IReplaySubjectImplementation
|
|
|
+ private sealed class ReplayOne : ReplayBufferBase
|
|
|
{
|
|
|
private bool _hasValue;
|
|
|
private T _value;
|
|
@@ -576,7 +637,7 @@ namespace System.Reactive.Subjects
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private sealed class ReplayMany : ReplayManyBase, IReplaySubjectImplementation
|
|
|
+ private sealed class ReplayMany : ReplayManyBase
|
|
|
{
|
|
|
private readonly int _bufferSize;
|
|
|
|
|
@@ -593,7 +654,7 @@ namespace System.Reactive.Subjects
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private sealed class ReplayAll : ReplayManyBase, IReplaySubjectImplementation
|
|
|
+ private sealed class ReplayAll : ReplayManyBase
|
|
|
{
|
|
|
public ReplayAll()
|
|
|
: base(0)
|
|
@@ -620,7 +681,7 @@ namespace System.Reactive.Subjects
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private abstract class ReplayManyBase : ReplayBufferBase, IReplaySubjectImplementation
|
|
|
+ private abstract class ReplayManyBase : ReplayBufferBase
|
|
|
{
|
|
|
protected readonly Queue<T> _queue;
|
|
|
|