diff --git a/src/ReactiveUI.Tests/ObservableAsPropertyHelper/ObservableAsPropertyHelperTest.cs b/src/ReactiveUI.Tests/ObservableAsPropertyHelper/ObservableAsPropertyHelperTest.cs
index 0d8e9cee11..f29e1c302a 100644
--- a/src/ReactiveUI.Tests/ObservableAsPropertyHelper/ObservableAsPropertyHelperTest.cs
+++ b/src/ReactiveUI.Tests/ObservableAsPropertyHelper/ObservableAsPropertyHelperTest.cs
@@ -290,10 +290,62 @@ int GetInitialValue()
Assert.Equal(42, result);
}
- ///
- /// Tests that Observable As Property Helpers initial value should emit initial value.
- ///
- /// The initial value.
+ /// Test that Observable As Property Helpers defers subscription with initial function value doesn't call on changed when subscribed.
+ /// The initial value.
+ [Theory]
+ [InlineData(default(int))]
+ [InlineData(42)]
+ public void OAPHDeferSubscriptionWithInitialFuncValueNotCallOnChangedWhenSubscribed(int initialValue)
+ {
+ var observable = Observable.Empty();
+
+ var wasOnChangingCalled = false;
+ Action onChanging = v => wasOnChangingCalled = true;
+ var wasOnChangedCalled = false;
+ Action onChanged = v => wasOnChangedCalled = true;
+
+ var fixture = new ObservableAsPropertyHelper(observable, onChanged, onChanging, () => initialValue, true);
+
+ Assert.False(fixture.IsSubscribed);
+ Assert.False(wasOnChangingCalled);
+ Assert.False(wasOnChangedCalled);
+
+ var result = fixture.Value;
+
+ Assert.True(fixture.IsSubscribed);
+ Assert.False(wasOnChangingCalled);
+ Assert.False(wasOnChangedCalled);
+ Assert.Equal(initialValue, result);
+ }
+
+ /// Test that Observable As Property Helpers defers subscription with initial function value doesn't call on changed when source provides initial value after subscription.
+ /// The initial value.
+ [Theory]
+ [InlineData(default(int))]
+ [InlineData(42)]
+ public void OAPHDeferSubscriptionWithInitialFuncValueNotCallOnChangedWhenSourceProvidesInitialValue(int initialValue)
+ {
+ var observable = new Subject();
+
+ var wasOnChangingCalled = false;
+ Action onChanging = v => wasOnChangingCalled = true;
+ var wasOnChangedCalled = false;
+ Action onChanged = v => wasOnChangedCalled = true;
+
+ var fixture = new ObservableAsPropertyHelper(observable, onChanged, onChanging, () => initialValue, true);
+
+ var result = fixture.Value;
+
+ Assert.Equal(initialValue, result);
+
+ observable.OnNext(initialValue);
+
+ Assert.False(wasOnChangingCalled);
+ Assert.False(wasOnChangedCalled);
+ }
+
+ /// Tests that Observable As Property Helpers initial value should emit initial value.
+ /// The initial value.
[Theory]
[InlineData(default(int))]
[InlineData(42)]
diff --git a/src/ReactiveUI/ObservableForProperty/ObservableAsPropertyHelper.cs b/src/ReactiveUI/ObservableForProperty/ObservableAsPropertyHelper.cs
index a0198ab5bf..20a15ec064 100644
--- a/src/ReactiveUI/ObservableForProperty/ObservableAsPropertyHelper.cs
+++ b/src/ReactiveUI/ObservableForProperty/ObservableAsPropertyHelper.cs
@@ -154,18 +154,23 @@ public ObservableAsPropertyHelper(
ex => _thrownExceptions.Value.OnNext(ex))
.DisposeWith(_disposable);
- _getInitialValue = getInitialValue!;
+ _getInitialValue = getInitialValue ??= () => default(T?);
if (deferSubscription)
{
- _lastValue = default;
- Source = observable.DistinctUntilChanged();
+ // Although there are no subscribers yet, we should skip all the values that are equal getInitialValue() instead of equal default(T?) because
+ // default(T?) is never accessible anyway when subscriptions are deferred. We're going to assume that the current value is getInitialValue() even
+ // if it hasn't been evaluated yet
+ Source = observable.SkipWhile(x => EqualityComparer.Default.Equals(x, getInitialValue() /* Don't use field to avoid capturing this */))
+ .DistinctUntilChanged();
}
else
{
_lastValue = _getInitialValue();
- Source = observable.StartWith(_lastValue).DistinctUntilChanged();
- Source.Subscribe(_subject).DisposeWith(_disposable);
+ Source = observable.StartWith(_lastValue)
+ .DistinctUntilChanged();
+ Source.Subscribe(_subject)
+ .DisposeWith(_disposable);
_activated = 1;
}
}
@@ -184,7 +189,7 @@ public T Value
if (localReferenceInCaseDisposeIsCalled is not null)
{
_lastValue = _getInitialValue();
- Source.StartWith(_lastValue).Subscribe(_subject).DisposeWith(localReferenceInCaseDisposeIsCalled);
+ Source.Subscribe(_subject).DisposeWith(localReferenceInCaseDisposeIsCalled);
}
}