尝试了解Subject<T>
,ReplaySubject<T>
和其他工作方式。这是一个例子:
(主题是可观察的和观察者)
public IObservable<int> CreateObservable()
{
Subject<int> subj = new Subject<int>(); // case 1
ReplaySubject<int> subj = new ReplaySubject<int>(); // case 2
Random rnd = new Random();
int maxValue = rnd.Next(20);
Trace.TraceInformation("Max value is: " + maxValue.ToString());
subj.OnNext(-1); // specific value
for(int iCounter = 0; iCounter < maxValue; iCounter++)
{
Trace.TraceInformation("Value: " + iCounter.ToString() + " is about to publish");
subj.OnNext(iCounter);
}
Trace.TraceInformation("Publish complete");
subj.OnComplete();
return subj;
}
public void Main()
{
//
// First subscription
CreateObservable()
.Subscribe(
onNext: (x)=>{
Trace.TraceInformation("X is: " + x.ToString());
});
//
// Second subscribe
CreateObservable()
.Subscribe(
onNext: (x2)=>{
Trace.TraceInformation("X2 is: " + x.ToString());
});
案例1:奇怪的情况是 - 当我使用Subject<T>
时没有订阅(???) - 我从未看到“X是:”文本 - 我只看到“值是:”和“最大值是”......为什么Subject<T>
不会将值推送到订阅状态?
案例2:如果我使用ReplaySubject<T>
- 我确实看到了订阅中的值,但我无法对任何内容应用Defer
选项。不是Subject
而不是Observable ....所以每个订阅都会收到不同的值,因为CreateObservable
函数是冷可观察的。 Defer
在哪里?
答案 0 :(得分:10)
每当你需要凭空创造一个可观察的时候,Observable.Create应该是第一个想到的东西。受试者在两种情况下进入图片:
您需要某种“可寻址端点”来提供数据,以便所有订阅者都能接收数据。将此与具有调用端(通过委托调用)和订阅端(通过委托与+ - 和 - =语法组合)的.NET事件进行比较。在很多情况下,你会发现使用Observable.Create可以达到同样的效果。
您需要在查询管道中多播消息,有效地在查询逻辑中通过许多分叉共享可观察序列,而不会触发多个预订。 (想想为你的宿舍订阅你最喜欢的杂志,并在信箱后面放一张照片复印机。你仍然支付一个订阅,虽然所有的朋友都可以通过信箱上的OnNext阅读杂志。)
此外,在很多情况下,Rx中已经有一个内置的原语可以完全满足你的需要。例如,From * factory方法与现有概念(例如事件,任务,异步方法,可枚举序列)桥接,其中一些使用了主题。对于组播逻辑的第二种情况,有发布,重播等系列的运算符。
答案 1 :(得分:4)
您需要注意代码的执行时间。
在“案例1”中,当您使用Subject<T>
时,您会注意到OnNext
&amp;的所有电话。在OnCompleted
方法返回observable之前完成CreateObservable
。由于您使用的是Subject<T>
,这意味着任何后续订阅都会错过所有值,因此您应该得到所得到的 - 没有。
在订阅观察者之前,您必须延迟对主题的操作。要使用Create
方法执行此操作。方法如下:
public IObservable<int> CreateObservable()
{
return Observable.Create<int>(o =>
{
var subj = new Subject<int>();
var disposable = subj.Subscribe(o);
var rnd = new Random();
var maxValue = rnd.Next(20);
subj.OnNext(-1);
for(int iCounter = 0; iCounter < maxValue; iCounter++)
{
subj.OnNext(iCounter);
}
subj.OnCompleted();
return disposable;
});
}
我删除了所有跟踪代码以获得简洁。
现在,对于每个订阅者,您都会在Create
方法中获得新代码,现在您将从内部Subject<T>
获取值。
使用Create
方法通常是创建从方法返回的observable的正确方法。
或者,您可以使用ReplaySubject<T>
并避免使用Create
方法。然而,由于许多原因,这没有吸引力。它强制在创建时计算整个序列。这给你一个冷酷的观察,你可以在不使用重播主题的情况下更有效地生成。
现在,除此之外,你应该尽量避免使用主题。一般规则是,如果你正在使用一个主题,那么你做错了什么。 CreateObservable
方法最好写成:
public IObservable<int> CreateObservable()
{
return Observable.Create<int>(o =>
{
var rnd = new Random();
var maxValue = rnd.Next(20);
return Observable.Range(-1, maxValue + 1).Subscribe(o);
});
}
根本不需要任何主题。
如果有帮助,请告诉我。