我有IObservable<ImmutableArray<T>>
,其中每个T
都有相应的删除命令IObservable<Unit>
。当用户在列表中的项目上单击“删除”时,我尝试响应。当一个项目添加到列表中时,我想开始监听(订阅)删除命令。当一个项目从列表中删除时,我想停止收听(取消订阅)。如果项目X被添加到列表中并且列表更改了很多次,我想确保我只在X上订阅了删除命令 - 当它被添加时 - 而不是每次列表更改时取消订阅并重新订阅
最初我尝试使用Switch
命令执行此操作。但后来我意识到,每次列表更改时,它可能都取消订阅并重新订阅每个项目。这使得难以使用Pairwise或Scan,因为每次列表更改时,我的订阅都会被删除,而我会从一个新的订阅开始。我认为我遇到了其他问题,但它通常看起来并不是所有订阅和取消订阅的正确答案。
所以我认为答案涉及使用TakeUntil
。我将监控列表,对其进行成对,并且能够始终知道什么是新的和已删除的。然后,当我订阅每个项目时,我将TakeUntil
该项目位于已移除的集合中。那就是这个想法,但我遇到了代码问题。
我正在与之合作:
interface IListItem {
IObservable<Unit> Delete { get; }
string ListItemName { get; }
}
IObservable<ImmutableList<IListItem>> _list;
_list...something here...Subscribe(i=>{
Console.WriteLine($"You requested to delete {i}!");
});
答案 0 :(得分:1)
嗯,我觉得我在这里得到了一个不错的答案。我想要的时间稍长,但我认为它有效。如果有人想建议更短或更简单的东西,我很乐意听到它。部分.Select(add => add.Delete.Select(_ => add).TakeUntil...
很奇怪。基本上,当为特定列表项调用delete命令时,我想返回调用Delete命令的列表项。
_items
.Pairwise((before, after) => new
{
AddedToList = after.Except(before),
RemovedFromList = before.Except(after)
})
.Publish(p =>
{
var additions = p.SelectMany(i => i.AddedToList);
var removals = p.SelectMany(i => i.RemovedFromList);
return
additions
.Select(add =>
add
.Delete
.Select(_ => add)
.TakeUntil(removals.Where(rem => rem == add)))
.Merge();
}).Subscribe(i =>
{
// process the delete request on i
// at the end, submit the modified array to _items
});
答案 1 :(得分:0)
这是我最终建立的。它对我来说非常好用。第一部分是通用课程,用于计算和报告两组之间的差异。
public class SetComparison<T>
{
private Lazy<IImmutableSet<T>> _added;
private Lazy<IImmutableSet<T>> _removed;
private Lazy<IImmutableSet<T>> _intersection;
public SetComparison(IEnumerable<T> previous, IEnumerable<T> current)
{
if (previous == null) throw new ArgumentNullException(nameof(previous));
if (current == null) throw new ArgumentNullException(nameof(current));
Previous = previous.ToImmutableHashSet();
Current = current.ToImmutableHashSet();
_added = new Lazy<IImmutableSet<T>>(() => Current.Except(Previous));
_removed = new Lazy<IImmutableSet<T>>(() => Previous.Except(Current));
_intersection = new Lazy<IImmutableSet<T>>(() => Current.Intersect(Previous));
}
public IImmutableSet<T> Previous { get; }
public IImmutableSet<T> Current { get; }
public IImmutableSet<T> Added => _added.Value;
public IImmutableSet<T> Removed => _removed.Value;
public IImmutableSet<T> Intersection => _intersection.Value;
}
这是一个像F#Pairwise这样的通用运算符,用于获取序列并将其转换为一系列重叠的项目。
public static IObservable<TResult> Pairwise<TSource, TResult>(
this IObservable<TSource> source,
Func<TSource, TSource, TResult> resultSelector) =>
source.Scan(
(default(TSource), default(TSource)),
(pair, current) => (pair.Item2, current))
.Skip(1)
.Select(p => resultSelector(p.Item1, p.Item2));
这现在成对了两套。
public static IObservable<SetComparison<T>> PairwiseSetComparison<T, TCollection>(this IObservable<TCollection> source) where TCollection : IEnumerable<T> =>
source
.Pairwise((a, b) => new SetComparison<T>(a, b));
public static IObservable<SetComparison<T>> PairwiseSetComparison<T>(this IObservable<ImmutableArray<T>> source) =>
source.Pairwise((a, b) => new SetComparison<T>(a, b));
最后,到
public static IObservable<TResult> Merge<TResult, T>(this IObservable<SetComparison<T>> source, Func<T, IObservable<TResult>> result) =>
source
.Publish(s =>
{
var additions = s.SelectMany(i => i.Added);
var removals = s.SelectMany(i => i.Removed);
return
additions
.Select(add => result(add).TakeUntil(removals.Where(rem => rem.Equals(add))))
.Merge();
});
所以现在当我想订阅仅在当前集合中的observable时,我会做这样的事情......
_items
.PairwiseSetComparison()
.Merge(i => i.Delete.Select(_ => i))
.Subscribe(i=> /* user clicked Delete on item i */)
完成所有这些操作的另一种方法是,当用户调用它时,列表中每个项目上的Delete ICommand实际上会运行一些委托。这样我就不必在调用命令时实际订阅了。不过,我更喜欢让我的清单中的物品完全不知道它们周围的环境。