From ead3c14da659a4ca38dc4b9d2d20d5d9249aa168 Mon Sep 17 00:00:00 2001 From: Yoshifumi Kawai Date: Wed, 17 Jan 2018 22:58:18 +0900 Subject: [PATCH 1/5] Add ReactivePropertySlim, ReadOnlyReactivePropertySlim --- .../ReactivePropertySlim.cs | 388 ++++++++++++++++++ .../ReactiveProperty.Tests.csproj | 4 +- .../ReactivePropertySlimTest.cs | 120 ++++++ .../ReadOnlyReactivePropertySlimTest.cs | 200 +++++++++ Test/ReactiveProperty.Tests/app.config | 32 +- Test/ReactiveProperty.Tests/packages.config | 8 +- 6 files changed, 731 insertions(+), 21 deletions(-) create mode 100644 Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs create mode 100644 Test/ReactiveProperty.Tests/ReactivePropertySlimTest.cs create mode 100644 Test/ReactiveProperty.Tests/ReadOnlyReactivePropertySlimTest.cs diff --git a/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs b/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs new file mode 100644 index 00000000..4a344dd1 --- /dev/null +++ b/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs @@ -0,0 +1,388 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.ComponentModel; +using System.Reactive.Disposables; +using System.Threading; + +namespace Reactive.Bindings +{ + // This file includes ReactivePropertySlim and ReadOnlyReactivePropertySlim. + + internal interface IObserverLinkedList + { + void UnsubscribeNode(ObserverNode node); + } + + internal sealed class ObserverNode : IObserver, IDisposable + { + readonly IObserver observer; + IObserverLinkedList list; + + public ObserverNode Previous { get; internal set; } + public ObserverNode Next { get; internal set; } + + public ObserverNode(IObserverLinkedList list, IObserver observer) + { + this.list = list; + this.observer = observer; + } + + public void OnNext(T value) + { + observer.OnNext(value); + } + + public void OnError(Exception error) + { + observer.OnError(error); + } + + public void OnCompleted() + { + observer.OnCompleted(); + } + + public void Dispose() + { + var sourceList = Interlocked.Exchange(ref list, null); + if (sourceList != null) + { + sourceList.UnsubscribeNode(this); + sourceList = null; + } + } + } + + public class ReactivePropertySlim : IReactiveProperty, IReadOnlyReactiveProperty, IObserverLinkedList + { + const int IsDisposedFlagNumber = 1 << 9; // (reserve 0 ~ 8) + + // minimize field count + T latestValue; + ReactivePropertyMode mode; // None = 0, DistinctUntilChanged = 1, RaiseLatestValueOnSubscribe = 2, Disposed = (1 << 9) + readonly EqualityComparer equalityComparer; + ObserverNode root; + ObserverNode last; + + public event PropertyChangedEventHandler PropertyChanged; + + public T Value + { + get + { + return latestValue; + } + set + { + if (IsDistinctUntilChanged && equalityComparer.Equals(latestValue, value)) + { + return; + } + + // Note:can set null and can set after disposed. + this.latestValue = value; + if (!IsDisposed) + { + OnNextAndRaiseValueChanged(ref value); + } + } + } + + public bool IsDisposed => (int)mode == IsDisposedFlagNumber; + + object IReactiveProperty.Value + { + get + { + return (object)Value; + } + set + { + Value = (T)value; + } + } + + object IReadOnlyReactiveProperty.Value + { + get + { + return (object)Value; + } + } + + bool IsDistinctUntilChanged => (mode & ReactivePropertyMode.DistinctUntilChanged) == ReactivePropertyMode.DistinctUntilChanged; + bool IsRaiseLatestValueOnSubscribe => (mode & ReactivePropertyMode.RaiseLatestValueOnSubscribe) == ReactivePropertyMode.RaiseLatestValueOnSubscribe; + + public ReactivePropertySlim(T initialValue = default(T), ReactivePropertyMode mode = ReactivePropertyMode.DistinctUntilChanged | ReactivePropertyMode.RaiseLatestValueOnSubscribe, EqualityComparer equalityComparer = null) + { + this.latestValue = initialValue; + this.mode = mode; + this.equalityComparer = equalityComparer ?? EqualityComparer.Default; + } + + void OnNextAndRaiseValueChanged(ref T value) + { + // call source.OnNext + var node = root; + while (node != null) + { + node.OnNext(value); + node = node.Next; + } + + this.PropertyChanged?.Invoke(this, SingletonPropertyChangedEventArgs.Value); + } + + public void ForceNotify() + { + OnNextAndRaiseValueChanged(ref latestValue); + } + + public IDisposable Subscribe(IObserver observer) + { + if (IsDisposed) + { + observer.OnCompleted(); + return Disposable.Empty; + } + + if (IsRaiseLatestValueOnSubscribe) + { + observer.OnNext(this.latestValue); + } + + // subscribe node, node as subscription. + var next = new ObserverNode(this, observer); + if (root == null) + { + root = last = next; + } + else + { + last.Next = next; + next.Previous = last; + last = next; + } + return next; + } + + void IObserverLinkedList.UnsubscribeNode(ObserverNode node) + { + if (node == root) + { + root = node.Next; + } + if (node == last) + { + last = node.Previous; + } + + if (node.Previous != null) + { + node.Previous.Next = node.Next; + } + if (node.Next != null) + { + node.Next.Previous = node.Previous; + } + } + + public void Dispose() + { + if (IsDisposed) return; + + var node = root; + root = last = null; + mode = (ReactivePropertyMode)IsDisposedFlagNumber; + + while (node != null) + { + node.OnCompleted(); + node = node.Next; + } + } + + // NotSupported validation. + + bool INotifyDataErrorInfo.HasErrors => throw new NotSupportedException(); + + IObservable IHasErrors.ObserveErrorChanged => throw new NotSupportedException(); + + IObservable IHasErrors.ObserveHasErrors => throw new NotSupportedException(); + + event EventHandler INotifyDataErrorInfo.ErrorsChanged + { + add + { + throw new NotSupportedException(); + } + + remove + { + throw new NotSupportedException(); + } + } + + IEnumerable INotifyDataErrorInfo.GetErrors(string propertyName) + { + throw new NotSupportedException(); + } + } + + public class ReadOnlyReactivePropertySlim : IReadOnlyReactiveProperty, IObserverLinkedList, IObserver + { + const int IsDisposedFlagNumber = 1 << 9; // (reserve 0 ~ 8) + + // minimize field count + T latestValue; + IDisposable sourceSubscription; + ReactivePropertyMode mode; // None = 0, DistinctUntilChanged = 1, RaiseLatestValueOnSubscribe = 2, Disposed = (1 << 9) + readonly EqualityComparer equalityComparer; + + ObserverNode root; + ObserverNode last; + + public event PropertyChangedEventHandler PropertyChanged; + public event EventHandler ErrorsChanged; + + public T Value + { + get + { + return latestValue; + } + } + + public bool IsDisposed => (int)mode == IsDisposedFlagNumber; + + object IReadOnlyReactiveProperty.Value + { + get + { + return (object)Value; + } + } + + bool IsDistinctUntilChanged => (mode & ReactivePropertyMode.DistinctUntilChanged) == ReactivePropertyMode.DistinctUntilChanged; + bool IsRaiseLatestValueOnSubscribe => (mode & ReactivePropertyMode.RaiseLatestValueOnSubscribe) == ReactivePropertyMode.RaiseLatestValueOnSubscribe; + + public ReadOnlyReactivePropertySlim(IObservable source, T initialValue = default(T), ReactivePropertyMode mode = ReactivePropertyMode.DistinctUntilChanged | ReactivePropertyMode.RaiseLatestValueOnSubscribe, EqualityComparer equalityComparer = null) + { + this.latestValue = initialValue; + this.mode = mode; + this.equalityComparer = equalityComparer ?? EqualityComparer.Default; + this.sourceSubscription = source.Subscribe(this); + } + + public IDisposable Subscribe(IObserver observer) + { + if (IsDisposed) + { + observer.OnCompleted(); + return Disposable.Empty; + } + + if (IsRaiseLatestValueOnSubscribe) + { + observer.OnNext(latestValue); + } + + // subscribe node, node as subscription. + var next = new ObserverNode(this, observer); + if (root == null) + { + root = last = next; + } + else + { + last.Next = next; + next.Previous = last; + last = next; + } + + return next; + } + + void IObserverLinkedList.UnsubscribeNode(ObserverNode node) + { + if (node == root) + { + root = node.Next; + } + if (node == last) + { + last = node.Previous; + } + + if (node.Previous != null) + { + node.Previous.Next = node.Next; + } + if (node.Next != null) + { + node.Next.Previous = node.Previous; + } + } + + public void Dispose() + { + if (IsDisposed) return; + + var node = root; + root = last = null; + mode = (ReactivePropertyMode)IsDisposedFlagNumber; + + while (node != null) + { + node.OnCompleted(); + node = node.Next; + } + sourceSubscription.Dispose(); + sourceSubscription = null; + } + + void IObserver.OnNext(T value) + { + if (IsDisposed) return; + + if (IsDistinctUntilChanged && equalityComparer.Equals(latestValue, value)) + { + return; + } + + // SetValue + this.latestValue = value; + + // call source.OnNext + var node = root; + while (node != null) + { + node.OnNext(value); + node = node.Next; + } + + // Notify changed. + this.PropertyChanged?.Invoke(this, SingletonPropertyChangedEventArgs.Value); + } + + void IObserver.OnError(Exception error) + { + // do nothing. + } + + void IObserver.OnCompleted() + { + // oncompleted same as dispose. + Dispose(); + } + } + + public static class ReadOnlyReactivePropertySlim + { + public static ReadOnlyReactivePropertySlim ToReadOnlyReactivePropertySlim(this IObservable source, T initialValue = default(T), ReactivePropertyMode mode = ReactivePropertyMode.DistinctUntilChanged | ReactivePropertyMode.RaiseLatestValueOnSubscribe, EqualityComparer equalityComparer = null) + { + return new ReadOnlyReactivePropertySlim(source, initialValue, mode, equalityComparer); + } + } +} \ No newline at end of file diff --git a/Test/ReactiveProperty.Tests/ReactiveProperty.Tests.csproj b/Test/ReactiveProperty.Tests/ReactiveProperty.Tests.csproj index 78ff7e2e..85dff187 100644 --- a/Test/ReactiveProperty.Tests/ReactiveProperty.Tests.csproj +++ b/Test/ReactiveProperty.Tests/ReactiveProperty.Tests.csproj @@ -12,7 +12,7 @@ Properties ReactiveProperty.Tests ReactiveProperty.Tests - v4.6.2 + v4.7 512 {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} false @@ -209,11 +209,13 @@ + + diff --git a/Test/ReactiveProperty.Tests/ReactivePropertySlimTest.cs b/Test/ReactiveProperty.Tests/ReactivePropertySlimTest.cs new file mode 100644 index 00000000..11453041 --- /dev/null +++ b/Test/ReactiveProperty.Tests/ReactivePropertySlimTest.cs @@ -0,0 +1,120 @@ +using Reactive.Bindings; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Reactive; +using System.Reactive.Linq; +using System.Threading.Tasks; +using System.Reactive.Subjects; + +namespace ReactiveProperty.Tests +{ + [TestClass] + public class ReactivePropertySlimTest + { + [TestMethod] + public void NormalCase() + { + var rp = new ReactivePropertySlim(); + rp.Value.IsNull(); + rp.Subscribe(x => x.IsNull()); + } + + [TestMethod] + public void InitialValue() + { + var rp = new ReactivePropertySlim("Hello world"); + rp.Value.Is("Hello world"); + rp.Subscribe(x => x.Is("Hello world")); + } + + [TestMethod] + public void NoRaiseLatestValueOnSubscribe() + { + var rp = new ReactivePropertySlim(mode: ReactivePropertyMode.DistinctUntilChanged); + var called = false; + rp.Subscribe(_ => called = true); + called.Is(false); + } + + [TestMethod] + public void NoDistinctUntilChanged() + { + var rp = new ReactivePropertySlim(mode: ReactivePropertyMode.RaiseLatestValueOnSubscribe); + var list = new List(); + rp.Subscribe(list.Add); + rp.Value = "Hello world"; + rp.Value = "Hello world"; + rp.Value = "Hello japan"; + list.Is(null, "Hello world", "Hello world", "Hello japan"); + } + + [TestMethod] + public void EnumCase() + { + var rp = new ReactivePropertySlim(); + var results = new List(); + rp.Subscribe(results.Add); + results.Is(TestEnum.None); + + rp.Value = TestEnum.Enum1; + results.Is(TestEnum.None, TestEnum.Enum1); + + rp.Value = TestEnum.Enum2; + results.Is(TestEnum.None, TestEnum.Enum1, TestEnum.Enum2); + } + + [TestMethod] + public void ForceNotify() + { + var rp = new ReactivePropertySlim(0); + var collecter = new List(); + rp.Subscribe(collecter.Add); + + collecter.Is(0); + rp.ForceNotify(); + collecter.Is(0, 0); + } + + [TestMethod] + public void UnsubscribeTest() + { + var rp = new ReactivePropertySlim(mode: ReactivePropertyMode.None); + var collecter = new List<(string, int)>(); + var a = rp.Select(x => ("a", x)).Subscribe(collecter.Add); + var b = rp.Select(x => ("b", x)).Subscribe(collecter.Add); + var c = rp.Select(x => ("c", x)).Subscribe(collecter.Add); + + rp.Value = 99; + collecter.Is(("a", 99), ("b", 99), ("c", 99)); + + collecter.Clear(); + a.Dispose(); + + rp.Value = 40; + collecter.Is(("b", 40), ("c", 40)); + + collecter.Clear(); + c.Dispose(); + + rp.Value = 50; + collecter.Is(("b", 50)); + + collecter.Clear(); + b.Dispose(); + + rp.Value = 9999; + collecter.Count.Is(0); + + var d = rp.Select(x => ("d", x)).Subscribe(collecter.Add); + + rp.Value = 9; + collecter.Is(("d", 9)); + + rp.Dispose(); + } + } +} diff --git a/Test/ReactiveProperty.Tests/ReadOnlyReactivePropertySlimTest.cs b/Test/ReactiveProperty.Tests/ReadOnlyReactivePropertySlimTest.cs new file mode 100644 index 00000000..e5464b35 --- /dev/null +++ b/Test/ReactiveProperty.Tests/ReadOnlyReactivePropertySlimTest.cs @@ -0,0 +1,200 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Reactive.Bindings; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Reactive.Disposables; +using System.Text; +using System.Threading.Tasks; + +namespace ReactiveProperty.Tests +{ + [TestClass] + public class ReadOnlyReactivePropertySlimTest + { + [TestMethod] + public void NormalPattern() + { + var s = new Subject(); + + var rp = s.ToReadOnlyReactivePropertySlim(); + var buffer = new List(); + rp.Subscribe(buffer.Add); + + rp.Value.IsNull(); + buffer.Count.Is(1); + buffer[0].IsNull(); + + s.OnNext("Hello"); + rp.Value.Is("Hello"); + buffer.Count.Is(2); + buffer.Is(default(string), "Hello"); + + s.OnNext("Hello"); + rp.Value.Is("Hello"); + buffer.Count.Is(2); // distinct until changed. + } + + [TestMethod] + public void MultiSubscribeTest() + { + var s = new Subject(); + + var rp = s.ToReadOnlyReactivePropertySlim(); + var buffer1 = new List(); + rp.Subscribe(buffer1.Add); + + + buffer1.Count.Is(1); + s.OnNext("Hello world"); + buffer1.Count.Is(2); + buffer1.Is(default(string), "Hello world"); + + var buffer2 = new List(); + rp.Subscribe(buffer2.Add); + buffer1.Is(default(string), "Hello world"); + buffer2.Is("Hello world"); + + s.OnNext("ReactiveProperty"); + buffer1.Is(default(string), "Hello world", "ReactiveProperty"); + buffer2.Is("Hello world", "ReactiveProperty"); + } + + [TestMethod] + public void NormalPatternNoDistinctUntilChanged() + { + var s = new Subject(); + + var rp = s.ToReadOnlyReactivePropertySlim( + mode: ReactivePropertyMode.RaiseLatestValueOnSubscribe); + var buffer = new List(); + rp.Subscribe(buffer.Add); + + rp.Value.IsNull(); + buffer.Count.Is(1); + buffer[0].IsNull(); + + s.OnNext("Hello"); + rp.Value.Is("Hello"); + buffer.Count.Is(2); + buffer.Is(default(string), "Hello"); + + s.OnNext("Hello"); + rp.Value.Is("Hello"); + buffer.Count.Is(3); // not distinct until changed. + } + + [TestMethod] + public void PropertyChangedTest() + { + var s = new Subject(); + var rp = s.ToReadOnlyReactivePropertySlim(); + var buffer = new List(); + rp.PropertyChanged += (_, args) => + { + buffer.Add(args.PropertyName); + }; + + buffer.Count.Is(0); + + s.OnNext("Hello"); + buffer.Count.Is(1); + + s.OnNext("Hello"); + buffer.Count.Is(1); + + s.OnNext("World"); + buffer.Count.Is(2); + } + + [TestMethod] + public void PropertyChangedNoDistinctUntilChangedTest() + { + var s = new Subject(); + var rp = s.ToReadOnlyReactivePropertySlim( + mode: ReactivePropertyMode.RaiseLatestValueOnSubscribe); + var buffer = new List(); + rp.PropertyChanged += (_, args) => + { + buffer.Add(args.PropertyName); + }; + + buffer.Count.Is(0); + + s.OnNext("Hello"); + buffer.Count.Is(1); + + s.OnNext("Hello"); + buffer.Count.Is(2); + + s.OnNext("World"); + buffer.Count.Is(3); + } + + [TestMethod] + public void BehaviorSubjectTest() + { + var s = new BehaviorSubject("initial value"); + var rp = s.ToReadOnlyReactivePropertySlim(); + rp.Value.Is("initial value"); + } + + [TestMethod] + public void ObservableCreateTest() + { + var i = 0; + var s = Observable.Create(ox => + { + i++; + return Disposable.Empty; + }); + + i.Is(0); + var rp = s.ToReadOnlyReactivePropertySlim(); + i.Is(1); + } + + [TestMethod] + public void UnsubscribeTest() + { + var source = new ReactivePropertySlim(mode: ReactivePropertyMode.None); + var rp = source.ToReadOnlyReactivePropertySlim(mode: ReactivePropertyMode.None); + + var collecter = new List<(string, int)>(); + var a = rp.Select(x => ("a", x)).Subscribe(collecter.Add); + var b = rp.Select(x => ("b", x)).Subscribe(collecter.Add); + var c = rp.Select(x => ("c", x)).Subscribe(collecter.Add); + + source.Value = 99; + collecter.Is(("a", 99), ("b", 99), ("c", 99)); + + collecter.Clear(); + a.Dispose(); + + source.Value = 40; + collecter.Is(("b", 40), ("c", 40)); + + collecter.Clear(); + c.Dispose(); + + source.Value = 50; + collecter.Is(("b", 50)); + + collecter.Clear(); + b.Dispose(); + + source.Value = 9999; + collecter.Count.Is(0); + + var d = rp.Select(x => ("d", x)).Subscribe(collecter.Add); + + source.Value = 9; + collecter.Is(("d", 9)); + + rp.Dispose(); + } + } +} diff --git a/Test/ReactiveProperty.Tests/app.config b/Test/ReactiveProperty.Tests/app.config index 4524b5c7..381a62d5 100644 --- a/Test/ReactiveProperty.Tests/app.config +++ b/Test/ReactiveProperty.Tests/app.config @@ -1,35 +1,35 @@ - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - + diff --git a/Test/ReactiveProperty.Tests/packages.config b/Test/ReactiveProperty.Tests/packages.config index 0cc67101..e366ffff 100644 --- a/Test/ReactiveProperty.Tests/packages.config +++ b/Test/ReactiveProperty.Tests/packages.config @@ -21,8 +21,8 @@ - - + + @@ -44,13 +44,13 @@ - + - + From 5041e3dd3d82d7a39ecc5f918cd2f016dd2a10a4 Mon Sep 17 00:00:00 2001 From: Yoshifumi Kawai Date: Wed, 17 Jan 2018 23:08:00 +0900 Subject: [PATCH 2/5] implement ToString to ReactivePropertySlim, ReadOnlyReactivePropertySlim --- .../ReactivePropertySlim.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs b/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs index 4a344dd1..e073831d 100644 --- a/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs +++ b/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs @@ -203,6 +203,13 @@ public void Dispose() } } + public override string ToString() + { + return (latestValue == null) + ? "null" + : latestValue.ToString(); + } + // NotSupported validation. bool INotifyDataErrorInfo.HasErrors => throw new NotSupportedException(); @@ -376,6 +383,13 @@ void IObserver.OnCompleted() // oncompleted same as dispose. Dispose(); } + + public override string ToString() + { + return (latestValue == null) + ? "null" + : latestValue.ToString(); + } } public static class ReadOnlyReactivePropertySlim From ce1546c868eb7cc3327e85ff7225ab95a41fea94 Mon Sep 17 00:00:00 2001 From: Yoshifumi Kawai Date: Wed, 17 Jan 2018 23:50:43 +0900 Subject: [PATCH 3/5] EqualityComparer -> IEqualityComparer --- Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs b/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs index e073831d..a9a98a0d 100644 --- a/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs +++ b/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs @@ -61,7 +61,7 @@ public class ReactivePropertySlim : IReactiveProperty, IReadOnlyReactivePr // minimize field count T latestValue; ReactivePropertyMode mode; // None = 0, DistinctUntilChanged = 1, RaiseLatestValueOnSubscribe = 2, Disposed = (1 << 9) - readonly EqualityComparer equalityComparer; + readonly IEqualityComparer equalityComparer; ObserverNode root; ObserverNode last; @@ -245,7 +245,7 @@ public class ReadOnlyReactivePropertySlim : IReadOnlyReactiveProperty, IOb T latestValue; IDisposable sourceSubscription; ReactivePropertyMode mode; // None = 0, DistinctUntilChanged = 1, RaiseLatestValueOnSubscribe = 2, Disposed = (1 << 9) - readonly EqualityComparer equalityComparer; + readonly IEqualityComparer equalityComparer; ObserverNode root; ObserverNode last; From bacf99a4d8235f010363387340338d6852322cca Mon Sep 17 00:00:00 2001 From: Yoshifumi Kawai Date: Wed, 17 Jan 2018 23:51:56 +0900 Subject: [PATCH 4/5] argument, too. --- Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs b/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs index a9a98a0d..75ab183a 100644 --- a/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs +++ b/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs @@ -114,7 +114,7 @@ object IReadOnlyReactiveProperty.Value bool IsDistinctUntilChanged => (mode & ReactivePropertyMode.DistinctUntilChanged) == ReactivePropertyMode.DistinctUntilChanged; bool IsRaiseLatestValueOnSubscribe => (mode & ReactivePropertyMode.RaiseLatestValueOnSubscribe) == ReactivePropertyMode.RaiseLatestValueOnSubscribe; - public ReactivePropertySlim(T initialValue = default(T), ReactivePropertyMode mode = ReactivePropertyMode.DistinctUntilChanged | ReactivePropertyMode.RaiseLatestValueOnSubscribe, EqualityComparer equalityComparer = null) + public ReactivePropertySlim(T initialValue = default(T), ReactivePropertyMode mode = ReactivePropertyMode.DistinctUntilChanged | ReactivePropertyMode.RaiseLatestValueOnSubscribe, IEqualityComparer equalityComparer = null) { this.latestValue = initialValue; this.mode = mode; @@ -274,7 +274,7 @@ object IReadOnlyReactiveProperty.Value bool IsDistinctUntilChanged => (mode & ReactivePropertyMode.DistinctUntilChanged) == ReactivePropertyMode.DistinctUntilChanged; bool IsRaiseLatestValueOnSubscribe => (mode & ReactivePropertyMode.RaiseLatestValueOnSubscribe) == ReactivePropertyMode.RaiseLatestValueOnSubscribe; - public ReadOnlyReactivePropertySlim(IObservable source, T initialValue = default(T), ReactivePropertyMode mode = ReactivePropertyMode.DistinctUntilChanged | ReactivePropertyMode.RaiseLatestValueOnSubscribe, EqualityComparer equalityComparer = null) + public ReadOnlyReactivePropertySlim(IObservable source, T initialValue = default(T), ReactivePropertyMode mode = ReactivePropertyMode.DistinctUntilChanged | ReactivePropertyMode.RaiseLatestValueOnSubscribe, IEqualityComparer equalityComparer = null) { this.latestValue = initialValue; this.mode = mode; @@ -394,7 +394,7 @@ public override string ToString() public static class ReadOnlyReactivePropertySlim { - public static ReadOnlyReactivePropertySlim ToReadOnlyReactivePropertySlim(this IObservable source, T initialValue = default(T), ReactivePropertyMode mode = ReactivePropertyMode.DistinctUntilChanged | ReactivePropertyMode.RaiseLatestValueOnSubscribe, EqualityComparer equalityComparer = null) + public static ReadOnlyReactivePropertySlim ToReadOnlyReactivePropertySlim(this IObservable source, T initialValue = default(T), ReactivePropertyMode mode = ReactivePropertyMode.DistinctUntilChanged | ReactivePropertyMode.RaiseLatestValueOnSubscribe, IEqualityComparer equalityComparer = null) { return new ReadOnlyReactivePropertySlim(source, initialValue, mode, equalityComparer); } From 0736e65da62d667ef43fd0dd8b1f5d6105246db6 Mon Sep 17 00:00:00 2001 From: Yoshifumi Kawai Date: Thu, 18 Jan 2018 00:00:42 +0900 Subject: [PATCH 5/5] remvoe ErrorsChanged event. --- Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs b/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs index 75ab183a..b3f74d76 100644 --- a/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs +++ b/Source/ReactiveProperty.NETStandard/ReactivePropertySlim.cs @@ -251,7 +251,6 @@ public class ReadOnlyReactivePropertySlim : IReadOnlyReactiveProperty, IOb ObserverNode last; public event PropertyChangedEventHandler PropertyChanged; - public event EventHandler ErrorsChanged; public T Value {