使用RX跟踪可观察集合中的多个项目

本文关键字:项目 集合 RX 跟踪 观察 使用 | 更新日期: 2023-09-27 18:15:29

我有一个简短的问题。我有一个ObservableCollection<IItem>,其中IItem有一个叫做Id的属性。在应用程序的整个生命周期中,项目被添加、删除,然后再次重新添加到此集合中。我需要的是跟踪什么时候项目与某些id是存在于这个集合。当所有必需的依赖项都存在时,我需要进行一些初始化,如果至少有一个必需的项被删除,那么我需要进行清理。如果该项再次被添加,那么我需要再次进行初始化。有什么建议使用什么RX操作符来构建这种查询吗?

使用RX跟踪可观察集合中的多个项目

跟踪集合的状态可能有点乏味。除非您的集合非常大,否则您可以在每次更改时检查集合,以确定是否满足了初始化的标准。然后,你可以使用DistinctUntilChanged来获得一个可观察对象,当你需要执行初始化和清理

时,它将被触发。

下面是一个例子:

var collection = new ObservableCollection<Int32>();
var observable = Observable
  .FromEventPattern<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>(
    handler => collection.CollectionChanged += handler,
    handler => collection.CollectionChanged -= handler
  );
然后需要一个谓词来确定是否需要初始化(集合"is ready")。如果您的集合很大,这个谓词可能会变得昂贵,因为它将在每次更改集合时调用,但我的假设是这不是问题。
Boolean IsReady(IEnumerable<Int32> items, IReadOnlyList<Int32> itemsRequiredToBeReady) {
  return items.Intersect(itemsRequiredToBeReady).Count() == itemsRequiredToBeReady.Count;
}

IsReady谓词从真变为假时,您可以使用DistinctUntilChanged来获取通知,反之亦然:

var isReadyObservable = observable
  .Select(ep => IsReady((ObservableCollection<Int32>) ep.Sender, ItemsRequiredToBeReady))
  .DistinctUntilChanged();

初始化和清除需要两个订阅:

isReadyObservable.Where(isReady => isReady).Subscribe(_ => Initialize());
isReadyObservable.Where(isReady => !isReady).Subscribe(_ => Cleanup());

ObservableCollection并不完全是可观察的,因此首先必须考虑在这种情况下将采用什么策略。如果只是添加和删除项目,那么这段代码应该足够了。

internal class Program
{
    private static ObservableCollection<IItem> oc = new ObservableCollection<IItem>();
    private static readonly long[] crossCheck = {1,2,3};
    private static void Main(string[] args)
    {
        oc.CollectionChanged += oc_CollectionChanged;
        oc.Add(new IItem {Id=1,Amount = 100});
        oc.Add(new IItem {Id=2,Amount = 200});
        oc.Add(new IItem {Id=3,Amount = 300});
        oc.RemoveAt(1);
    }
    private static void oc_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
    {
        Console.WriteLine("{0} {1}", e.Action, oc.Sum(s1 => s1.Amount));
        if (crossCheck.SequenceEqual(oc.Select(s1 => s1.Id).Intersect(crossCheck)))
            Console.WriteLine("I have all elements I wanted!");
        if (e.OldItems != null && e.Action.Equals(NotifyCollectionChangedAction.Remove) &&
            e.OldItems.Cast<IItem>().Any(a1 => a1.Id.Equals(2))) Console.WriteLine("I've lost item two");
    }
}
internal class IItem
{
    public long Id { get; set; }
    public int Amount { get; set; }
}

生产:

Add 100
Add 300
Add 600
I have all elements I wanted!
Remove 400
I've lost item two
Press any key to continue . . .

当然,在事件处理程序中,您可以根据需要处理其他条件,例如,您可能希望仅触发一次这些数据相关事件,等等。