如何在保持订阅完整的同时更改Observable
来源?是的,使用Subject
很容易实现,但最佳做法是不使用主题。
这是一个例子:
public class Worker
{
public IObservable<Worker> IsWorking {get;}
public string Name {get;}
public Worker(string name)
{
Name = name;
IsWorking = Observable.Interval(TimeSpan.FromSeconds(1))
.Select(_ => this);
}
}
此类公开了Observable
每秒触发一次。
public class WorkerState
{
public Worker CurrentWorker
{
set => WorkerIsBusy = value.IsWorking;
}
public IObservable<Worker> WorkerIsBusy { get; private set; }
}
此类应该包含一个工作单元,可以动态更改,但Subscription
应自动附加到当前工作程序。 (这将是Subject
)
在这里测试:
public class Program
{
public async Task Run()
{
var state = new WorkerState();
state.CurrentWorker = new Worker("A");
state.WorkerIsBusy.Subscribe(worker =>
Console.WriteLine($"Worker {worker.Name}"));
await Task.Delay(3000);
Console.WriteLine("Should change now to B...");
state.CurrentWorker = new Worker("B");
Console.Read();
}
}
但我的输出如下:
Worker A
Worker A
Should change now to B...
Worker A
Worker A
Worker A
...
而不是切换到工人B.
答案 0 :(得分:2)
您几乎总是用来重新订阅的方法是使用.Switch()
运算符让您重新订阅查询。
这样的事情:
var switcher = new Subject<IObservable<int>>();
var subscription = switcher.Switch().Subscribe(x => Console.WriteLine(x));
switcher.OnNext(Observable.Return(42));
switcher.OnNext(Observable.Range(0, 3));
产生:
42 0 1 2
无需重新订阅。
对于您的代码,您需要将WorkerState
更改为:
public class WorkerState
{
private Worker currentWorker = null;
private Subject<Worker> currentWorkerSubject = new Subject<Worker>();
public Worker CurrentWorker
{
set
{
currentWorker = value;
currentWorkerSubject.OnNext(value);
}
}
public IObservable<Worker> WorkerIsBusy
{
get =>
currentWorkerSubject
.StartWith(currentWorker)
.Where(w => w != null)
.Select(w => w.IsWorking)
.Switch();
}
}
然后您的代码有效(稍微警告await Task.Delay(3000);
与Observable.Interval(TimeSpan.FromSeconds(1))
内Worker
的竞争条件较小。将await
更改为3500
避免它。
答案 1 :(得分:0)
您遇到的问题是您已订阅了之前的WorkerIsBusy值,虽然您已更改了引用,但您尚未更改订阅,该订阅仍指向之前的值。
您需要拦截(实际订阅)worker.IsWorking在您的WorkerState类中,并在属性值更改时取消订阅/重新订阅。
或者你可以使用主题 - 我不明白为什么你不会?