【问题标题】:Is there a way to specify initial values to reactive extensions observables?有没有办法为响应式扩展 observables 指定初始值?
【发布时间】:2013-11-03 03:01:06
【问题描述】:

在响应式和响应式扩展中,您可以将多个可观察对象合并为一个。使用诸如 CombineLatest 或 Zip 之类的函数,您可以使生成的 observable 使用一个函数来根据组合 observable 的值计算结果。尽管 CombineLatest 为您提供了一个 observable,它在每次任何组合的 observable 触发时都会向您发送通知,但它仅在所有这些 observable 至少触发一次时才开始工作。我希望它从一开始就发出通知 - 给组合的 observable 一个初始值或其他东西。当然,我可以以某种方式使那些可观察到的火来设置初始值。但有时它并不那么容易,或者它使代码看起来很难看。这是一个例子: 我正在使用reactiveui 制作一个wpf 应用程序。在我的一个视图模型中,我试图通过组合 observables 来创建属性。所以,我有一个具有 NumberToSum 属性的对象集合(ReactiveList),我需要一个属性来显示该属性值的总和。该集合中的对象也是视图模型,并且它们的 NumberToSum 属性可以由用户更改。总和的对象数量由其他属性决定。我就是这样尝试的:

private readonly ReactiveList<Element> _collection = new ReactiveList<Element>();
private readonly ObservableAsPropertyHelper<int> _sum;
private int _elementsToTake = 0;

public int ElementsToTake
{
    get { return _elementsToTake; }
    set { this.RaiseAndSetIfChanged(ref _elementsToTake, value) }
}

public int Sum
{
    get { return _sum.Value; }
}

public MyViewModel
{
    var elementsToTakeObservable = this
        .WhenAny(x => x.ElementsToTake, x => x.Value);
    _collection.ChangeTrackingEnabled = true;
    //here I'm adding some objects to _collection
    var sumObservable = _collection.ItemChanged
        .CombineLatest(elementsToTakeObservable,
            (_, c) => _collection.Take(c)
                .Aggregate(0, (i, x) => i + x.NumberToSum))).
        .ToProperty(this, x => x.Sum, out _sum, 0);
}

所以,我使用 ReactiveList.ItemChanged 来跟踪 _collection 项目的变化。每次发生更改时,都会重新计算总和。这里的问题是组合成 sumObservable 的 observable 的初始值。我可以通过将值分配给 ElementsToTake 属性来轻松设置 elementsToTakeObservable 的初始值,这将引发属性更改事件和内容。但是对于 _collection.ItemChanged observable 来说,做到这一点并不容易。我需要让 _collection 中的至少一个对象引发 propertychanged 事件,但这看起来很糟糕。 是否可以设置这些初始值?或者也许我这样做完全错了?

【问题讨论】:

    标签: c# wpf reactiveui


    【解决方案1】:

    StartWith 将解决您的问题。此外,我不确定您是否需要 ItemChanged,它会在列表中的项目 更改时发出信号,而不是在添加或删除项目时发出信号。在这里,您可能还想听Changed

    虽然尝试以这种方式保持“运行总数”很诱人,但由于重置和移动,实际上很难正确地做到这一点。一种更简单的方法是滥用 CPU 非常快的事实,并在每次更改时重新计算它:

    _collection.ChangeTrackingEnabled = true;
    
    Observable.Merge(
             _collection.Changed.Select(_ => Unit.Default), 
             _collection.ItemChanged.Select(_ => Unit.Default))
        .Select(_ => _collection.Sum(x => x.NumberToSum));
    

    另一种对这样的聚合不是很好但也非常有用的方法是ActOnEveryObject,它将对列表中的每个项目执行一个块,并正确处理初始化、重置和移动。

    【讨论】:

    • 感谢您提供将 ItemChanged 与 Change 结合使用的提示。 StartWith 没有解决我的问题。或者我只是不明白如何正确使用它。可以举个例子吗?
    • StartWith 不是您上面的代码唯一有问题的地方,但不包括它是 CombineLatest 似乎“不起作用”的一个常见原因,因为直到它对两个“边”都有一个值,它不会产生任何东西
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-15
    • 1970-01-01
    • 2021-08-25
    • 1970-01-01
    • 1970-01-01
    • 2010-09-13
    相关资源
    最近更新 更多