使用RX跟踪可观察集合中的多个项目
本文关键字:项目 集合 RX 跟踪 观察 使用 | 更新日期: 2023-09-27 18:15:29
我有一个简短的问题。我有一个ObservableCollection<IItem>
,其中IItem
有一个叫做Id的属性。在应用程序的整个生命周期中,项目被添加、删除,然后再次重新添加到此集合中。我需要的是跟踪什么时候项目与某些id是存在于这个集合。当所有必需的依赖项都存在时,我需要进行一些初始化,如果至少有一个必需的项被删除,那么我需要进行清理。如果该项再次被添加,那么我需要再次进行初始化。有什么建议使用什么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 . . .
当然,在事件处理程序中,您可以根据需要处理其他条件,例如,您可能希望仅触发一次这些数据相关事件,等等。