在我的程序中,我有Room
的概念,每个房间都有一些User
,每个用户都有一个Observable
,表示他想要房间里的安静程度,当用户选择一个新值时,我只需在User
类中的内部BehaviorSubject
示例上调用OnNext
,将其推送到Observable。
这个程序的主要逻辑是,用户应该能够知道房间需要“什么程度的安静”,例如,如果房间里至少有一个用户需要安静,那么整个房间都需要安静。
这是我的类的一个简化:
首先,我使用一个枚举来表示可能的声级,现在只有两个值:
enum SoundLevelRequirement
{
/// <summary>
/// Indicates that the user is ok with any sound level in the room.
/// </summary>
None,
/// <summary>
/// Indicates that the user needs absolute silence in the room.
/// </summary>
Silence
}
用户只需要公开一个Observable,每当他的状态发生变化时,Observable就会发出滴答声。
class User
{
private readonly BehaviorSubject<SoundLevelRequirement> soundLevel;
public User(string name)
{
Name = name;
soundLevel = new BehaviorSubject<SoundLevelRequirement>(SoundLevelRequirement.None);
}
public string Name { get; }
public IObservable<SoundLevelRequirement> SoundLevelChanged => soundLevel.DistinctUntilChanged();
public void SetSoundLevel(SoundLevelRequirement level)
{
soundLevel.OnNext(level);
}
}
而房间类基本上是用户的一个容器,应该有自己的可观察项来表示整个房间的整体状态:
class Room
{
private readonly BehaviorSubject<SoundLevelRequirement> soundLevel;
private readonly ObservableCollection<User> users;
public Room(string name)
{
Name = name;
Users = new ObservableCollection<User>();
// THIS IS WHERE I NEED TO COMBINE ALL CHILD OBSERVABLES INSTEAD
// OF USING ANOTHER Subject
soundLevel = new BehaviorSubject<SoundLevelRequirement>(SoundLevelRequirement.None);
}
public ObservableCollection<User> Users { get; set; }
public string Name { get; }
public IObservable<SoundLevelRequirement> SoundLevel => soundLevel.DistinctUntilChanged();
}
我在将使用Rx的用户Observable
直接合并到另一个Observable
时遇到了麻烦,因为解决方案的动态特性。(新的User
对象可以添加到房间或从房间中移除),所以我没有一个静态的可观察对象列表可以使用。通过利用CombineLatest
,然后在其上执行我的过滤逻辑,可以非常容易地实现,如下所示:
Users.Select(u => u.SoundLevelChanged).CombineLatest().Select(latest => latest.Max());
这样,每当用户状态发生变化时,我只需要看看“最大价值”是什么,就可以确定房间的状态,但一旦我添加或删除用户,我就需要让它与可观察到的同步,所以这是行不通的。
我还需要确保在用户离开房间时进行适当的处理。例如,如果一个5人的房间由于一个用户选择了Silence
而处于“Silence”状态,则必须在该用户离开时重置房间状态,并将其设置回None
。
我曾考虑过使用ObservableCollection
来监视添加和删除,但我无法在不重新创建可观察对象的情况下提出一些东西,这显然是行不通的,因为已经有人订阅了更改。
2条答案
按热度按时间ljsrvy3e1#
如果可以的话,我会尽量简化,只需对类
User
做一个简单的修改即可。用户需要添加此属性:
现在可以像这样实现
Room
:如果您需要让
Room.SoundLevel
具有重播1,则将.Replay(1)
添加到soundLevel
字段中,但它必须成为IConnectableObservable<SoundLevelRequirement>
才能使其正常工作。然后,您需要在Room
上实现IDisposable
以释放连接。这是正确的方法-您应该尽可能避免主题。它们我只会让你的代码更难处理。xxb16uws2#
我知道这个问题已经很老了,但是我想指出的是,使用动态数据中的扩展方法,可以用一种很简单的方式编写这个问题。
财产
需要按照
Enigmativity
的建议添加到User
类中,然后您只需编写在
Room
类的构造函数中。