我不理解Finally方法。在这种情况下它不会发射。
[TestMethod]
public void FinallyHappensOnError()
{
bool finallyActionHappened = false;
try
{
Observable
.Throw<Unit>(new DivideByZeroException())
.Finally(() => finallyActionHappened = true)
.Subscribe();
}
catch
{
}
Assert.IsTrue(finallyActionHappened);
}
这个使用Do而不是Finally。我不明白为什么它适用于Do而不是最终。
[TestMethod]
public void CanRecordWhenSequenceFinishes()
{
bool sequenceFinished = false;
try
{
Observable.Throw<Unit>(new DivideByZeroException())
.Do(
onError: ex => { sequenceFinished = true; },
onCompleted: () => sequenceFinished = true,
onNext: _ => { })
.Subscribe();
}
catch
{
}
Assert.IsTrue(sequenceFinished);
}
答案 0 :(得分:2)
您的代码(两种方式)都是竞争条件。竞争条件使用.Do
以及.Finally
的错误方式解决了正确的方式。为什么不如何避免它:
public async Task FinallyHappensOnError()
{
bool finallyActionHappened = false;
try
{
await Observable.Throw<Unit>(new DivideByZeroException())
.Finally(() => finallyActionHappened = true);
}
catch
{
}
Assert.IsTrue(finallyActionHappened);
}
或者,如果您不想使用TPL / async / await:
[TestMethod]
public void FinallyHappensOnError()
{
bool finallyActionHappened = false;
try
{
Observable
.Throw<Unit>(new DivideByZeroException())
.Finally(() => finallyActionHappened = true)
.Subscribe(
_ => {},
() => Assert.IsTrue(finallyActionHappened)
);
}
catch
{
}
}
答案 1 :(得分:0)
在您的示例中未调用 Finally
操作的原因是,通过调用不包含 .Subscribe()
处理程序的“裸”onError
重载订阅了可观察序列.当 onError
处理程序丢失且序列失败时,在遇到异常的任何线程上同步抛出异常。在您的情况下,您很“幸运”并且在当前线程上抛出了异常,因此您可以使用 catch
块捕获它。否则,异常将无法处理,并使进程崩溃。您可以通过像这样修改示例来测试此条件:
Observable
.Throw<Unit>(new DivideByZeroException(), ThreadPoolScheduler.Instance)
.Finally(() => finallyActionHappened = true)
.Subscribe();
Thread.Sleep(500); // Give some time to the unhandled exception to emerge
道德故事是,您应该避免在未提供 onError
处理程序的情况下订阅序列,除非您对发生的坏事感到满意,例如未调用 Finally
操作或进程崩溃。要“修复”您的测试,您只需要提供一个 onError
处理程序,如下所示:
[TestMethod]
public void FinallyHappensOnError()
{
bool finallyActionHappened = false;
Observable
.Throw<Unit>(new DivideByZeroException())
.Finally(() => finallyActionHappened = true)
.Subscribe(value => { }, error => { }, () => { });
Assert.IsTrue(finallyActionHappened);
}
注意如何不再需要空的 catch
块。异常的处理现在由空的 onError
处理程序执行。
顺便说一句,这个测试的断言成功只是因为可观察序列在订阅期间同步完成。在具有更长生命周期的可观察序列的一般情况下,断言将失败,因为 Assert.IsTrue
将在序列完成之前被调用。出于这个原因,我建议在检查断言之前等待可观察序列同步或异步完成。下面是一个同步等待的例子,这也意味着一个完整的订阅,并附加了所有三个处理程序。
Observable
.Throw<Unit>(new DivideByZeroException())
.Finally(() => finallyActionHappened = true)
.DefaultIfEmpty().Wait();
Wait
运算符会同步重新抛出异常,因此您可能希望像在原始测试中一样 try
/catch
。