我有一个类ObservableCollection<T>
代表一个不断变化的集合:
public interface IObservableCollection<T> : IObservable<IEnumerable<T>>
{
void Add(T item);
void Remove(T item);
}
在添加或删除某个项目时,Subject<IEnumerable<T>>
内部调用了OnNext
方法,并使用IEnumerable<T>
Subscribe
方法通过IObservableCollection<T>
方法调用该方法}}
我还有一个班级Person
:
public interface IPerson
{
string Name { get; }
IObservable<int> Position { get; }
}
我想要做的是产生一个IEnumerable<Tuple<string, int>>
流,代表每个人的位置,一个人在集合中。这似乎相对简单:
var peopleCollectionStream = new ObservableCollection<IPerson>();
var peoplePositions = from people in peopleCollectionStream
from updateList in
(from person in people
select person.Position.Select(pos => Tuple.Create(person.Name, pos)))
.CombineLatest()
select updateList;
我现在可以像这样订阅流:
peoplePositions
.Subscribe(people =>
{
Console.WriteLine("Something was updated");
foreach (var personTuple in people)
Console.WriteLine("{0} -> {1}", personTuple.Item1, personTuple.Item2);
});
我得到了所需的输出:
var alice = new Person() { Name = "Alice" };
peopleCollectionStream.Add(alice); // Alice -> 0
alice.Move(2); // Alice -> 2
var bob = new Person() { Name = "Bob" };
peopleCollectionStream.Add(bob); // Alice -> 2, Bob -> 0
bob.Move(3); // Alice -> 2, Bob -> 3
如果我希望从收藏中删除某个人,并因此从流中排除他们的更新,就会出现问题:
peopleCollectionStream.Remove(bob); // Alice -> 2
bob.Move(4); // Alice -> 2, Bob -> 4
如果将Bob的位置更新从集合中删除,我想阻止其被包括在内。我怎么能这样做?
答案 0 :(得分:1)
我发现尝试使用添加和删除事件是一个坏主意,如果你想做这些功能性事情。将删除与添加相匹配,并确保底层代码也是如此,这是很多工作。
我所做的是使用易腐物品/收藏品。我将每个项目与生命周期(取消令牌)配对,并且该项目在其生命周期结束时被视为已删除。然后我在连接其他东西时使用那些生命周期。我使用了一种名为PerishableCollection<T>
的集合类型,它将项目与生命周期配对,并允许您将其内容视为IObservable<Perishable<T>>
。
我wrote a blog post about perishable collections,并发布了nuget library you can reference。
这里的代码应该展现易腐变集的易腐变集:
public static PerishableCollection<T> Flattened<T>(this PerishableCollection<PerishableCollection<T>> collectionOfCollections, Lifetime lifetimeOfResult) {
if (collectionOfCollections == null) throw new ArgumentNullException("collectionOfCollections");
var flattenedCollection = new PerishableCollection<T>();
collectionOfCollections.CurrentAndFutureItems().Subscribe(
c => c.Value.CurrentAndFutureItems().Subscribe(
// OnItem: include in result, but prevent lifetimes from exceeding source's lifetime
e => flattenedCollection.Add(
item: e.Value,
lifetime: e.Lifetime.Min(c.Lifetime)),
// subscription to c ends when the c's lifetime ends or result is no longer needed
c.Lifetime.Min(lifetimeOfResult)),
// subscription ends when result is no longer needed
lifetimeOfResult);
return flattenedCollection;
}
上述工作通过订阅接收集合添加到集合集合,然后为订阅接收项目的每个集合。这些物品被放置在最终的集合中,其寿命在物品死亡或其集合消亡时结束。当给予方法的生命周期终止时,所有订阅都会消失。
解决此问题的另一种方法是编写一个方法来展平IObservable<Perishable<IObservable<Perishable<T>>>>
。这样做的好处是不需要调用者明确地管理结果的生命周期,并且适用于更多情况。但是,这种方法写起来要困难得多,因为你必须以线程安全的方式处理序列失败/完成。
以下是使用flatten方法的示例(创建一个新的控制台应用程序,引用易坏的集合,粘贴上述方法以及此方法):
using TwistedOak.Collections;
using TwistedOak.Util;
static void Main() {
var p = new PerishableCollection<PerishableCollection<string>>();
var f = p.Flattened(Lifetime.Immortal);
f.CurrentAndFutureItems().Subscribe(e => {
Console.WriteLine("{0} added to flattened", e.Value);
e.Lifetime.WhenDead(() => Console.WriteLine("{0} removed from flattened", e.Value));
});
// add some 'c' items to f via p
var c = new PerishableCollection<string>();
var cLife = new LifetimeSource();
c.Add("candy", Lifetime.Immortal);
p.Add(c, cLife.Lifetime);
c.Add("cane", Lifetime.Immortal);
// add some 'd' items to f via p
var d = new PerishableCollection<string>();
p.Add(d, Lifetime.Immortal);
d.Add("door", Lifetime.Immortal);
d.Add("dock", Lifetime.Immortal);
// should remove c's items from f via removing c from p
cLife.EndLifetime();
}
代码应输出:
candy added to flattened
cane added to flattened
door added to flattened
dock added to flattened
candy removed from flattened
cane removed from flattened
希望这足以让你开始走上更轻松的道路。
答案 1 :(得分:0)
答案是.Switch
运算符。通过仅选择要订阅的最新可观察列表,该流将排除最新版本集合中不存在的任何内容:
var peoplePositions = (from people in peopleCollectionStream
select
(from person in people
select person.Position
.Select(pos => Tuple.Create(person.Name, pos))
).CombineLatest()
).Switch();
(顺便说一下,如果有人在使用括号/嵌套的linq查询语法时对格式有任何好的建议,请告诉我,因为上面看起来非常糟糕!)