为什么不能观察到。最后被召唤?

时间:2017-11-10 21:16:05

标签: c# system.reactive

我不理解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);
}

2 个答案:

答案 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