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

时间:2014-07-29 12:30:54

标签: c# system.reactive

这是一个简短的问题。我有一个ObservableCollection<IItem>,其中IItem有一个名为Id的属性。在整个应用程序的生命周期中,项目将被添加,删除,然后再次重新添加到此集合中。 我需要的是跟踪此集合中是否存在具有特定ID的项目。当存在所有必需的依赖项时,我需要进行一些初始化,如果至少删除了一个必需项,那么我需要进行清理。如果该项再次重新添加,那么我需要再次进行初始化。 有什么建议RX运营商用来构建这种查询吗?

2 个答案:

答案 0 :(得分:1)

跟踪收集的状态可能有点单调乏味。除非您的集合非常大,否则您可以检查每次更改的集合,以确定是否满足初始化条件。然后,您可以使用DistinctUntilChanged来获取在需要执行初始化和清理时将触发的可观察对象

以下是一个例子:

var collection = new ObservableCollection<Int32>();
var observable = Observable
  .FromEventPattern<NotifyCollectionChangedEventHandler, NotifyCollectionChangedEventArgs>(
    handler => collection.CollectionChanged += handler,
    handler => collection.CollectionChanged -= handler
  );

然后,您需要一个判断是否需要初始化的谓词(集合“已准备就绪”)。如果你的集合很大,这个谓词可能会变得昂贵,因为每次对集合的更改都会调用它,但我的假设是这不是问题。

Boolean IsReady(IEnumerable<Int32> items, IReadOnlyList<Int32> itemsRequiredToBeReady) {
  return items.Intersect(itemsRequiredToBeReady).Count() == itemsRequiredToBeReady.Count;
}

然后,当DistinctUntilChanged谓词从true更改为false时,您可以使用IsReady来收到通知,反之亦然:

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

要初始化和清理,您需要两个订阅:

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

答案 1 :(得分:0)

ObservableCollection不能像it turns out那样被观察到,所以首先你必须考虑在这种情况下你将采用什么策略。如果只是添加和删除项目,则此代码应该足够。

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 . . .

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