Browse Source

Added the BehaviorSubject<T>.Value property, which extracts the current value.

davedev 12 years ago
parent
commit
d9e0677754

+ 32 - 0
Rx.NET/Source/System.Reactive.Linq/Reactive/Subjects/BehaviorSubject.cs

@@ -11,6 +11,38 @@ namespace System.Reactive.Subjects
     /// <typeparam name="T">The type of the elements processed by the subject.</typeparam>
     public sealed class BehaviorSubject<T> : ISubject<T>, IDisposable
     {
+        /// <summary>
+        /// Gets the current value or throws an exception.
+        /// </summary>
+        /// <value>The initial value passed to the constructor until <see cref="OnNext"/> is called; after which, the last value passed to <see cref="OnNext"/>.</value>
+        /// <remarks>
+        /// <para><see cref="Value"/> is frozen after <see cref="OnCompleted"/> is called.</para>
+        /// <para>After <see cref="OnError"/> is called, <see cref="Value"/> always throws the specified exception.</para>
+        /// <para>An exception is always thrown after <see cref="Dispose"/> is called.</para>
+        /// <alert type="caller">
+        /// Reading <see cref="Value"/> is a thread-safe operation, though there's a potential race condition when <see cref="OnNext"/> or <see cref="OnError"/> are being invoked concurrently.
+        /// In some cases, it may be necessary for a caller to use external synchronization to avoid race conditions.
+        /// </alert>
+        /// </remarks>
+        /// <exception cref="ObjectDisposedException">Dispose was called.</exception>
+        public T Value
+        {
+            get
+            {
+                lock (_gate)
+                {
+                    CheckDisposed();
+
+                    if (_exception != null)
+                    {
+                        throw _exception;
+                    }
+
+                    return _value;
+                }
+            }
+        }
+
         private readonly object _gate = new object();
 
         private ImmutableList<IObserver<T>> _observers;

+ 71 - 0
Rx.NET/Source/Tests.System.Reactive/Tests/Linq/Subjects/BehaviorSubjectTest.cs

@@ -431,5 +431,76 @@ namespace ReactiveTests.Tests
             s.OnError(new Exception());
             Assert.IsFalse(s.HasObservers);
         }
+
+        [TestMethod]
+        public void Value_Initial()
+        {
+            var s = new BehaviorSubject<int>(42);
+            Assert.AreEqual(42, s.Value);
+        }
+
+        [TestMethod]
+        public void Value_First()
+        {
+            var s = new BehaviorSubject<int>(42);
+            Assert.AreEqual(42, s.Value);
+
+            s.OnNext(43);
+            Assert.AreEqual(43, s.Value);
+        }
+
+        [TestMethod]
+        public void Value_Second()
+        {
+            var s = new BehaviorSubject<int>(42);
+            Assert.AreEqual(42, s.Value);
+
+            s.OnNext(43);
+            Assert.AreEqual(43, s.Value);
+
+            s.OnNext(44);
+            Assert.AreEqual(44, s.Value);
+        }
+
+        [TestMethod]
+        public void Value_FrozenAfterOnCompleted()
+        {
+            var s = new BehaviorSubject<int>(42);
+            Assert.AreEqual(42, s.Value);
+
+            s.OnNext(43);
+            Assert.AreEqual(43, s.Value);
+
+            s.OnNext(44);
+            Assert.AreEqual(44, s.Value);
+
+            s.OnCompleted();
+            Assert.AreEqual(44, s.Value);
+
+            s.OnNext(1234);
+            Assert.AreEqual(44, s.Value);
+        }
+
+        [TestMethod, ExpectedException(typeof(InvalidOperationException))]
+        public void Value_ThrowsAfterOnError()
+        {
+            var s = new BehaviorSubject<int>(42);
+            Assert.AreEqual(42, s.Value);
+
+            s.OnError(new InvalidOperationException());
+            
+            Assert.Fail("Should not be able to read Value: {0}", s.Value);
+        }
+
+        [TestMethod, ExpectedException(typeof(ObjectDisposedException))]
+        public void Value_ThrowsOnDispose()
+        {
+            var s = new BehaviorSubject<int>(42);
+            Assert.AreEqual(42, s.Value);
+
+            s.Dispose();
+
+            Assert.Fail("Should not be able to read Value: {0}", s.Value);
+        }
     }
 }