Skip to content

Commit 340e7b2

Browse files
authored
Remove Default values from WhenAny dependents (#3967)
<!-- Please be sure to read the [Contribute](https://github.com/reactiveui/reactiveui#contribute) section of the README --> **What kind of change does this PR introduce?** <!-- Bug fix, feature, docs update, ... --> update **What is the current behavior?** <!-- You can also link to an open issue here. --> Default values are used which can lead to incorrect runtime operation **What is the new behavior?** <!-- If this is a feature change --> Default Default values from ObservableForProperty Remove Default values from SubscribeToExpressionChain **What might this PR break?** BREAKING CHANGE - ObservableForProperty and SubscribeToExpressionChain no longer use default values Any named default values will need to be removed, then an appropriate overload selected **Please check if the PR fulfills these requirements** - [ ] Tests for the changes have been added (for bug fixes / features) - [ ] Docs have been added / updated (for bug fixes / features) **Other information**:
1 parent fc79b8c commit 340e7b2

7 files changed

+2317
-347
lines changed

src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.DotNet8_0.verified.txt

Lines changed: 98 additions & 46 deletions
Large diffs are not rendered by default.

src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.DotNet9_0.verified.txt

Lines changed: 98 additions & 46 deletions
Large diffs are not rendered by default.

src/ReactiveUI.Tests/API/ApiApprovalTests.ReactiveUI.Net4_7.verified.txt

Lines changed: 98 additions & 46 deletions
Large diffs are not rendered by default.

src/ReactiveUI.Tests/WhenAny/ReactiveNotifyPropertyChangedMixinTest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ public void OFPNamedPropertyTestNoSkipInitial() =>
197197
{
198198
IsOnlyOneWord = "Pre"
199199
};
200-
fixture.ObservableForProperty(x => x.IsOnlyOneWord, skipInitial: false)
200+
fixture.ObservableForProperty(x => x.IsOnlyOneWord, false, false)
201201
.ToObservableChangeSet(ImmediateScheduler.Instance)
202202
.Bind(out var changes)
203203
.Subscribe();

src/ReactiveUI/Mixins/ReactiveNotifyPropertyChangedMixin.cs

Lines changed: 189 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,75 @@ public static class ReactiveNotifyPropertyChangedMixin
2323

2424
static ReactiveNotifyPropertyChangedMixin() => RxApp.EnsureInitialized();
2525

26+
/// <summary>
27+
/// ObservableForProperty returns an Observable representing the
28+
/// property change notifications for a specific property on a
29+
/// ReactiveObject. This method (unlike other Observables that return
30+
/// IObservedChange) guarantees that the Value property of
31+
/// the IObservedChange is set.
32+
/// </summary>
33+
/// <typeparam name="TSender">The sender type.</typeparam>
34+
/// <typeparam name="TValue">The value type.</typeparam>
35+
/// <param name="item">The source object to observe properties of.</param>
36+
/// <param name="property">An Expression representing the property (i.e.
37+
/// 'x =&gt; x.SomeProperty.SomeOtherProperty'.</param>
38+
/// <returns>
39+
/// An Observable representing the property change
40+
/// notifications for the given property.
41+
/// </returns>
42+
public static IObservable<IObservedChange<TSender, TValue>> ObservableForProperty<TSender, TValue>(
43+
this TSender? item,
44+
Expression<Func<TSender, TValue>> property) => ObservableForProperty(item, property, false, true, true);
45+
46+
/// <summary>
47+
/// ObservableForProperty returns an Observable representing the
48+
/// property change notifications for a specific property on a
49+
/// ReactiveObject. This method (unlike other Observables that return
50+
/// IObservedChange) guarantees that the Value property of
51+
/// the IObservedChange is set.
52+
/// </summary>
53+
/// <typeparam name="TSender">The sender type.</typeparam>
54+
/// <typeparam name="TValue">The value type.</typeparam>
55+
/// <param name="item">The source object to observe properties of.</param>
56+
/// <param name="property">An Expression representing the property (i.e.
57+
/// 'x =&gt; x.SomeProperty.SomeOtherProperty'.</param>
58+
/// <param name="beforeChange">If True, the Observable will notify
59+
/// immediately before a property is going to change.</param>
60+
/// <returns>
61+
/// An Observable representing the property change
62+
/// notifications for the given property.
63+
/// </returns>
64+
public static IObservable<IObservedChange<TSender, TValue>> ObservableForProperty<TSender, TValue>(
65+
this TSender? item,
66+
Expression<Func<TSender, TValue>> property,
67+
bool beforeChange) => ObservableForProperty(item, property, beforeChange, true, true);
68+
69+
/// <summary>
70+
/// ObservableForProperty returns an Observable representing the
71+
/// property change notifications for a specific property on a
72+
/// ReactiveObject. This method (unlike other Observables that return
73+
/// IObservedChange) guarantees that the Value property of
74+
/// the IObservedChange is set.
75+
/// </summary>
76+
/// <typeparam name="TSender">The sender type.</typeparam>
77+
/// <typeparam name="TValue">The value type.</typeparam>
78+
/// <param name="item">The source object to observe properties of.</param>
79+
/// <param name="property">An Expression representing the property (i.e.
80+
/// 'x =&gt; x.SomeProperty.SomeOtherProperty'.</param>
81+
/// <param name="beforeChange">If True, the Observable will notify
82+
/// immediately before a property is going to change.</param>
83+
/// <param name="skipInitial">If true, the Observable will not notify
84+
/// with the initial value.</param>
85+
/// <returns>
86+
/// An Observable representing the property change
87+
/// notifications for the given property.
88+
/// </returns>
89+
public static IObservable<IObservedChange<TSender, TValue>> ObservableForProperty<TSender, TValue>(
90+
this TSender? item,
91+
Expression<Func<TSender, TValue>> property,
92+
bool beforeChange,
93+
bool skipInitial) => ObservableForProperty(item, property, beforeChange, skipInitial, true);
94+
2695
/// <summary>
2796
/// ObservableForProperty returns an Observable representing the
2897
/// property change notifications for a specific property on a
@@ -47,9 +116,9 @@ public static class ReactiveNotifyPropertyChangedMixin
47116
public static IObservable<IObservedChange<TSender, TValue>> ObservableForProperty<TSender, TValue>(
48117
this TSender? item,
49118
Expression<Func<TSender, TValue>> property,
50-
bool beforeChange = false,
51-
bool skipInitial = true,
52-
bool isDistinct = true)
119+
bool beforeChange,
120+
bool skipInitial,
121+
bool isDistinct)
53122
{
54123
property.ArgumentNullExceptionThrowIfNull(nameof(property));
55124

@@ -76,6 +145,34 @@ public static IObservable<IObservedChange<TSender, TValue>> ObservableForPropert
76145
isDistinct);
77146
}
78147

148+
/// <summary>
149+
/// ObservableForProperty returns an Observable representing the
150+
/// property change notifications for a specific property on a
151+
/// ReactiveObject, running the IObservedChange through a Selector
152+
/// function.
153+
/// </summary>
154+
/// <typeparam name="TSender">The sender type.</typeparam>
155+
/// <typeparam name="TValue">The value type.</typeparam>
156+
/// <typeparam name="TRet">The return value type.</typeparam>
157+
/// <param name="item">The source object to observe properties of.</param>
158+
/// <param name="property">An Expression representing the property (i.e.
159+
/// 'x => x.SomeProperty'.</param>
160+
/// <param name="selector">A Select function that will be run on each
161+
/// item.</param>
162+
/// <returns>An Observable representing the property change
163+
/// notifications for the given property.</returns>
164+
public static IObservable<TRet> ObservableForProperty<TSender, TValue, TRet>(
165+
this TSender? item,
166+
Expression<Func<TSender, TValue>> property,
167+
Func<TValue?, TRet> selector) // TODO: Create Test
168+
where TSender : class
169+
{
170+
property.ArgumentNullExceptionThrowIfNull(nameof(property));
171+
selector.ArgumentNullExceptionThrowIfNull(nameof(selector));
172+
173+
return item.ObservableForProperty(property, false).Select(x => selector(x.Value));
174+
}
175+
79176
/// <summary>
80177
/// ObservableForProperty returns an Observable representing the
81178
/// property change notifications for a specific property on a
@@ -98,7 +195,7 @@ public static IObservable<TRet> ObservableForProperty<TSender, TValue, TRet>(
98195
this TSender? item,
99196
Expression<Func<TSender, TValue>> property,
100197
Func<TValue?, TRet> selector,
101-
bool beforeChange = false) // TODO: Create Test
198+
bool beforeChange) // TODO: Create Test
102199
where TSender : class
103200
{
104201
property.ArgumentNullExceptionThrowIfNull(nameof(property));
@@ -107,6 +204,90 @@ public static IObservable<TRet> ObservableForProperty<TSender, TValue, TRet>(
107204
return item.ObservableForProperty(property, beforeChange).Select(x => selector(x.Value));
108205
}
109206

207+
/// <summary>
208+
/// Creates a observable which will subscribe to the each property and sub property
209+
/// specified in the Expression. eg It will subscribe to x =&gt; x.Property1.Property2.Property3
210+
/// each property in the lambda expression. It will then provide updates to the last value in the chain.
211+
/// </summary>
212+
/// <typeparam name="TSender">The type of the origin of the expression chain.</typeparam>
213+
/// <typeparam name="TValue">The end value we want to subscribe to.</typeparam>
214+
/// <param name="source">The object where we start the chain.</param>
215+
/// <param name="expression">A expression which will point towards the property.</param>
216+
/// <returns>
217+
/// A observable which notifies about observed changes.
218+
/// </returns>
219+
/// <exception cref="InvalidCastException">If we cannot cast from the target value from the specified last property.</exception>
220+
public static IObservable<IObservedChange<TSender, TValue>> SubscribeToExpressionChain<TSender, TValue>(
221+
this TSender? source,
222+
Expression? expression) // TODO: Create Test
223+
=> SubscribeToExpressionChain<TSender, TValue>(source, expression, false, true, false, true);
224+
225+
/// <summary>
226+
/// Creates a observable which will subscribe to the each property and sub property
227+
/// specified in the Expression. eg It will subscribe to x =&gt; x.Property1.Property2.Property3
228+
/// each property in the lambda expression. It will then provide updates to the last value in the chain.
229+
/// </summary>
230+
/// <typeparam name="TSender">The type of the origin of the expression chain.</typeparam>
231+
/// <typeparam name="TValue">The end value we want to subscribe to.</typeparam>
232+
/// <param name="source">The object where we start the chain.</param>
233+
/// <param name="expression">A expression which will point towards the property.</param>
234+
/// <param name="beforeChange">If we are interested in notifications before the property value is changed.</param>
235+
/// <returns>
236+
/// A observable which notifies about observed changes.
237+
/// </returns>
238+
/// <exception cref="InvalidCastException">If we cannot cast from the target value from the specified last property.</exception>
239+
public static IObservable<IObservedChange<TSender, TValue>> SubscribeToExpressionChain<TSender, TValue>(
240+
this TSender? source,
241+
Expression? expression,
242+
bool beforeChange) // TODO: Create Test
243+
=> SubscribeToExpressionChain<TSender, TValue>(source, expression, beforeChange, true, false, true);
244+
245+
/// <summary>
246+
/// Creates a observable which will subscribe to the each property and sub property
247+
/// specified in the Expression. eg It will subscribe to x =&gt; x.Property1.Property2.Property3
248+
/// each property in the lambda expression. It will then provide updates to the last value in the chain.
249+
/// </summary>
250+
/// <typeparam name="TSender">The type of the origin of the expression chain.</typeparam>
251+
/// <typeparam name="TValue">The end value we want to subscribe to.</typeparam>
252+
/// <param name="source">The object where we start the chain.</param>
253+
/// <param name="expression">A expression which will point towards the property.</param>
254+
/// <param name="beforeChange">If we are interested in notifications before the property value is changed.</param>
255+
/// <param name="skipInitial">If we don't want to get a notification about the default value of the property.</param>
256+
/// <returns>
257+
/// A observable which notifies about observed changes.
258+
/// </returns>
259+
/// <exception cref="InvalidCastException">If we cannot cast from the target value from the specified last property.</exception>
260+
public static IObservable<IObservedChange<TSender, TValue>> SubscribeToExpressionChain<TSender, TValue>(
261+
this TSender? source,
262+
Expression? expression,
263+
bool beforeChange,
264+
bool skipInitial) // TODO: Create Test
265+
=> SubscribeToExpressionChain<TSender, TValue>(source, expression, beforeChange, skipInitial, false, true);
266+
267+
/// <summary>
268+
/// Creates a observable which will subscribe to the each property and sub property
269+
/// specified in the Expression. eg It will subscribe to x =&gt; x.Property1.Property2.Property3
270+
/// each property in the lambda expression. It will then provide updates to the last value in the chain.
271+
/// </summary>
272+
/// <typeparam name="TSender">The type of the origin of the expression chain.</typeparam>
273+
/// <typeparam name="TValue">The end value we want to subscribe to.</typeparam>
274+
/// <param name="source">The object where we start the chain.</param>
275+
/// <param name="expression">A expression which will point towards the property.</param>
276+
/// <param name="beforeChange">If we are interested in notifications before the property value is changed.</param>
277+
/// <param name="skipInitial">If we don't want to get a notification about the default value of the property.</param>
278+
/// <param name="suppressWarnings">If true, no warnings should be logged.</param>
279+
/// <returns>
280+
/// A observable which notifies about observed changes.
281+
/// </returns>
282+
/// <exception cref="InvalidCastException">If we cannot cast from the target value from the specified last property.</exception>
283+
public static IObservable<IObservedChange<TSender, TValue>> SubscribeToExpressionChain<TSender, TValue>(
284+
this TSender? source,
285+
Expression? expression,
286+
bool beforeChange,
287+
bool skipInitial,
288+
bool suppressWarnings) // TODO: Create Test
289+
=> SubscribeToExpressionChain<TSender, TValue>(source, expression, beforeChange, skipInitial, suppressWarnings, true);
290+
110291
/// <summary>
111292
/// Creates a observable which will subscribe to the each property and sub property
112293
/// specified in the Expression. eg It will subscribe to x =&gt; x.Property1.Property2.Property3
@@ -127,10 +308,10 @@ public static IObservable<TRet> ObservableForProperty<TSender, TValue, TRet>(
127308
public static IObservable<IObservedChange<TSender, TValue>> SubscribeToExpressionChain<TSender, TValue>(
128309
this TSender? source,
129310
Expression? expression,
130-
bool beforeChange = false,
131-
bool skipInitial = true,
132-
bool suppressWarnings = false,
133-
bool isDistinct = true) // TODO: Create Test
311+
bool beforeChange,
312+
bool skipInitial,
313+
bool suppressWarnings,
314+
bool isDistinct) // TODO: Create Test
134315
{
135316
IObservable<IObservedChange<object?, object?>> notifier =
136317
Observable.Return(new ObservedChange<object?, object?>(null, null, source));

0 commit comments

Comments
 (0)