C# 为什么是可观察的。当可观察时最终不调用。生成结束?

C# 为什么是可观察的。当可观察时最终不调用。生成结束?,c#,task-parallel-library,system.reactive,C#,Task Parallel Library,System.reactive,我需要在两个状态之间切换,每个状态具有不同的间隔时间。 我能想到的最好的方法是使用反应式扩展的Observable.Generate 这真是太可怕了 根据我在msdn和其他网站上看到的,如果 可观察的“优雅地或例外地终止”。我正在测试以下代码 (在LINQPad中)查看它是如何工作的,但我根本无法启动.Finall() var ia = TimeSpan.FromSeconds(1); var ib = TimeSpan.FromSeconds(.2); var start = DateTime

我需要在两个状态之间切换,每个状态具有不同的间隔时间。
我能想到的最好的方法是使用反应式扩展的Observable.Generate 这真是太可怕了

根据我在msdn和其他网站上看到的,如果 可观察的“优雅地或例外地终止”。我正在测试以下代码 (在LINQPad中)查看它是如何工作的,但我根本无法启动.Finall()

var ia = TimeSpan.FromSeconds(1);
var ib = TimeSpan.FromSeconds(.2);
var start = DateTime.UtcNow;
var ct = new CancellationTokenSource();

var o = Observable.Generate(
    true,
//    s => !ct.IsCancellationRequested,
    s => (DateTime.UtcNow-start) < TimeSpan.FromSeconds(3) && !ct.IsCancellationRequested,
    s => !s,
    s => s ? "on" : "off",
    s => s? ib : ia)
//    .TakeUntil(start+TimeSpan.FromSeconds(3))
    .Concat(Observable.Return("end"));


o.Subscribe( s=> s.Dump(), ct.Token);
var t = o.ToTask(ct.Token);


t.ContinueWith(x => x.Dump("done"));
o.Finally(() => "finallY".Dump()); // never gets called?

Thread.Sleep(10000);
ct.Cancel();
var ia=TimeSpan.FromSeconds(1);
var ib=从秒开始的时间跨度(.2);
var start=DateTime.UtcNow;
var ct=新的CancellationTokenSource();
var o=可观察的。生成(
是的,
//s=>!ct.IsCancellationRequested,
s=>(DateTime.UtcNow start)!s,
s=>s?“开”:“关”,
s=>s?ib:ia)
//.TakeUntil(开始+时间间隔从秒开始(3))
.Concat(可观察到的回报(“结束”);
o、 订阅(s=>s.Dump(),ct.Token);
var t=o.ToTask(ct.Token);
t、 继续(x=>x.Dump(“完成”);
o、 Finally(()=>“Finally”.Dump());//从来没有人打过电话?
睡眠(10000);
ct.Cancel();
如果我让Thread.Sleep 10秒,可观察序列完成,任务继续启动, 但不是。最后()

如果我使用Thread.Sleep 2s,可观察序列将被取消,Task.ContinueWith将再次激发, 但不是。最后()


为什么不呢?

最后查看
方法的返回类型;应该给你一个提示。就像
Concat
方法返回一个新的
IObservable
,并将新序列连接到它,但不改变原始序列一样,
Finally
方法返回一个新的
IObservable
,该新的
IObservable
具有最终操作,但您订阅的是原始的
IObservable
。在你的
Subscribe
电话前面放上下面的一行,它就可以工作了

o = o.Finally(() => "finallY".Dump());
但我同意这是一个奇怪的API选择;我认为
最终
更类似于
订阅
,而不是
Concat
。您正在订阅最终的“事件”;奇怪的是,API强迫您创建一个全新的IObservable,然后订阅它,以便最终实现
。此外,它还允许一个潜在的错误(如果我们使用您问题中的函数,这一点很明显),即如果您订阅两次新的IObservable,那么您的
最终将执行两次。因此,您必须确保其中一个订阅在“最终”IObservable上,其他订阅都在原始订阅上。看起来很不寻常


我想思考这个问题的方式是,
最终
并不是要修改可观察对象,而是要修改订阅本身。i、 例如,他们通常不希望您公开访问具有
Finally
内容的命名可观察对象(
var o=Observable.[…])。Finally(…);
),而是希望它与订阅调用本身相关联(
var subscription=o.Finally(…)。Subscribe(…);
)啊,这是有意义的。但是,避免Finally的多个调用的一种方法是不要替换为原始的IObservable,而只对单个订阅使用新的IObservable。只需将一行更改为:
o.Finally(()=>“Finally”.Dump()).Subscribe()