0

Im using ReactiveProperty library in my code and from time to time I need to attach two ReactiveProperty<T> together maintaining property data synchronized. For example by combining a ReactivePropertySlim from a service class to a ReactiveProperty in a ViewModel class.

Usually I use the next code:

// NewProperty is a ViewModel public property
NewProperty = service.Property.ToReactiveProperty<T>();
var propertyDisposable = NewProperty.Subscribe(value => service.Property.Value = value);

Not lookup so bad for a single property but when the number gets high the code gets to.

For now Im using a simple extension method to limit the code repetition.

public static (IReactiveProperty<T> property, IDisposable cleanup) AttachReactiveProperty<T>(this IReactiveProperty<T> baseProperty)
{
    var newProperty = baseProperty.ToReactiveProperty<T>();
    var cleanup = newProperty.Subscribe(value => baseProperty.Value = value);
    return (newProperty, cleanup);
}

I end with a property variable and a IDisposable variable to manage unsubscrition.

var (pausedProperty, pausedDisposable) = remoteConversion.Paused.AttachReactiveProperty();
NewProperty = pausedProperty;

For now the extension is doing his work (less and clear code I think). But is there a better way to approach this problem.

  • [OAPH](https://www.reactiveui.net/docs/handbook/observable-as-property-helper/) is a much cleaner way of dealing with dependent properties, imo. – Blindy Feb 17 '22 at 19:35
  • Sorry but Im not using ReactiveUI in any way, if this is what you mean. But I give you +10 for comment speed ;) – Alexei Agüero Alba Feb 17 '22 at 19:49

2 Answers2

0

Well I found a similar but more clean (I think) way to the the same.

In each reactive classes I usually have some CompositeDisposable _cleanup field like this.

public sealed class SomeClass: IDisposable
{
    private readonly CompositeDisposable _cleanup = new CompositeDisposable();
    
    public void Dispose()
    {
        _cleanup.Dispose();
    }
}

Then using this basic class contruction and this extension method

internal static class ReactivePropertyEx
{
    public static IReactiveProperty<T> AttachReactiveProperty<T>(this IReactiveProperty<T> baseProperty, Action<IDisposable> registerDisposable)
    {
        var newProperty = baseProperty.ToReactiveProperty<T>();

        var cleanup = newProperty.Subscribe(value => baseProperty.Value = value);

        registerDisposable(cleanup);

        return newProperty;
    }
}

The code ends more simple and elegant. The usage is

NewProperty = service.Property.AttachReactiveProperty(disposable => disposable.AddTo(_cleanup));
0

In this case, you can use ToReactiveProperty[Slim]AsSynchronized extension method that is defined Reactive.Bindings.Extensions namespace.

using Reactive.Bindings;
using Reactive.Bindings.Extensions;

var rp1 = new ReactiveProperty<string>("Init");
ReactiveProperty<string> rp2 = rp1.ToReactivePropertyAsSynchronized(x => x.Value);
ReactivePropertySlim<string> rp3 = rp1.ToReactivePropertySlimAsSynchronized(x => x.Value);
Console.WriteLine("----");
Console.WriteLine(rp1.Value); // Init
Console.WriteLine(rp2.Value); // Init
Console.WriteLine(rp3.Value); // Init

rp1.Value = "Update from rp1";
Console.WriteLine("----");
Console.WriteLine(rp1.Value); // Update from rp1
Console.WriteLine(rp2.Value); // Update from rp1
Console.WriteLine(rp3.Value); // Update from rp1

rp3.Value = "Update from rp3";
Console.WriteLine("----");
Console.WriteLine(rp1.Value); // Update from rp3
Console.WriteLine(rp2.Value); // Update from rp3
Console.WriteLine(rp3.Value); // Update from rp3

// disconnect
rp2.Dispose();
rp3.Dispose();

rp1.Value = "Done";
Console.WriteLine("----");
Console.WriteLine(rp1.Value); // Done
Console.WriteLine(rp2.Value); // Update from rp3
Console.WriteLine(rp3.Value); // Update from rp3

See: https://okazuki.jp/ReactiveProperty/features/Work-together-with-plane-model-layer-objects.html#two-way-synchronization