Search code examples
c#reactiveui

Should multiple .WhenAny(...).ToPropertyEx(...) calls overwrite one another?


I have an object with two WhenAnyValues:

this.WhenAnyValue(x => x.SomeInt)
    .Select(x => "Bar" + x)
    .ToPropertyEx(this, x => x.SomeString);
                
this.WhenAnyValue(x => x.SomeInt)
    .Select(x => "Foo" + x)
    .ToPropertyEx(this, x => x.SomeString);

Testing with 1-4 as inputs yields:

1
Foo0
Foo1
2
Foo2
3
Foo3
4
Foo4

Swapping the order of the WhenAnyValue methods, such that the Bar comes last, results in only Bars being triggered. So it seems that the later WhenAnyValue is somehow overwriting the first one. Is this a bug or intended behavior? In case this is a bug, this is from ReactiveUI version 16.3.10.

This issue also appears if the items are filtered with, for instance, Foo only setting on even values and Bar only setting on odd values. You still only get whichever WhenAnyValue was the last to be called.

The Foo0 is due to lazy execution, and not related to this issue.


Solution

  • The issue is not the WhenAnyValue() call, it can be used multiple times on the same property and each of them will fire the observed value change separately. The issue is the ToPropertyEx() call or the underlying ToProperty() call it redirects to.

    The point of ToProperty() or ToPropertyEx() is to build a value subscription for a specific target property (here SomeString). It will configure the subscription and fire a PropertyChanged event on value changes. The getter you have for your SomeString property will be linked to one generated ObservableAsPropertyHelper<T> instance, either with explicit code like

    public string SomeString => this.someString.Value;
    

    (where this.someString is of type ObservableAsPropertyHelper<string>) or by using the attribute [ObservableAsProperty] of the Reactive.Fody library like

    [ObservableAsProperty]
    public string SomeString {get;}
    

    There can only be one "getter" configured and it is either the ObservableAsPropertyHelper build from your "Bar" subscription or your "Foo" subscription. Keep in mind, the generated PropertyChangedEvent event does not carry the actual value, only the property name that was changed. Subscribers have to get the value from somewhere and they use the getter of that property.