【问题标题】:Using RX to track multiple items in an observable collection使用 RX 跟踪可观察集合中的多个项目
【发布时间】:2014-07-29 21:22:03
【问题描述】:

这是一个简单的问题。我有一个ObservableCollection<IItem>,其中IItem 有一个名为Id 的属性。在应用程序的整个生命周期中,项目被添加、删除,然后再次重新添加到此集合中。 我需要的是跟踪此集合中何时出现具有某些 id 的项目。当所有必需的依赖项都存在时,我需要进行一些初始化,如果至少删除了一个必需项,那么我需要进行清理。如果该项目然后再次重新添加,那么我需要再次进行初始化。 有什么建议可以使用哪些 RX 运算符来构建此类查询?

【问题讨论】:

    标签: c# system.reactive


    【解决方案1】:

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

    这是一个例子:

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

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

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

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

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

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-01-19
        • 1970-01-01
        • 2015-11-30
        • 1970-01-01
        • 1970-01-01
        • 2010-11-04
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多