附加两个 ReactiveProperty 的更好方法

Better way to attach two ReactiveProperty

我在我的代码中使用 ReactiveProperty 库,有时我需要将两个 ReactiveProperty<T> 连接在一起以保持 属性 数据同步。例如,通过将服务 class 中的 ReactivePropertySlim 组合到 ViewModel class.

中的 ReactiveProperty

通常我使用下一个代码:

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

单个 属性 的查找不是那么糟糕,但是当数字变大时,代码就会到达。

目前我使用简单的扩展方法来限制代码重复。

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);
}

我以一个 属性 变量和一个 IDisposable 变量结尾来管理退订。

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

现在扩展正在做他的工作(我认为代码更少更清晰)。但是有没有更好的方法来解决这个问题。

好吧,我找到了一个类似但更干净(我认为)的方法。

在每个反应式 classes 中,我通常有一些像这样的 CompositeDisposable _cleanup 字段。

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

然后使用这个基本的class构造和这个扩展方法

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;
    }
}

代码结束更简洁优雅。用法是

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

在这种情况下,您可以使用在 Reactive.Bindings.Extensions 命名空间中定义的 ToReactiveProperty[Slim]AsSynchronized 扩展方法。

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

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