4

テスト 1 に合格します。Test2 と Test3 が失敗するのはなぜですか? .NET 4.0 と Rx 2.0 を使用しています。

[TestClass]
public class RxQuestion
{
   private Subject<string> sequence;
   [TestInitialize] public void Intialize() { sequence = new Subject<string>(); }
   [TestMethod] public void Test1() { Test(sequence); }
   [TestMethod] public void Test2() { Test(sequence.Where(s => true)); }
   [TestMethod] public void Test3() { Test(sequence.OfType<string>()); }
   private void Test(IObservable<string> observable)
   {
      var observed = string.Empty;
      observable.Subscribe(s => { observed = s; if (s == "a") throw new Exception(); });
      try { sequence.OnNext("a"); } catch { }
      sequence.OnNext("b");
      Assert.AreEqual("b", observed);
   }
}
4

2 に答える 2

13

私にとっての本当の疑問は、なぜ Test1 がパスするのかということです。私には、このSubject<T>型は の他のすべての実装と同じルールに従っていないように見えIObservable<T>ます。

より詳細な検査 (実際にはリフレクション) ではSubject<T>、DotPeek/Reflector で型を分離して、呼び出しが行われたときにそのインスタンスOnNext(T)に直接委任されていることを確認できます。_observerサブスクリプションの前は、これは単なる NullObject/NopObserver です。サブスクリプションが作成された後 (一般に)、オブザーバーはObserver<T>実装です。この実装は、実際にはIObserver<T>インターフェースの複合パターン実装でありOnNext(T)、その各インスタンスを呼び出すだけです。

さらに、OnNext ハンドラーを受け取るだけの Subscribe の拡張メソッドを使用していると考えると、 の実際の実装IObserver<T>AnonymousObserver<T>. これを開くと、 への呼び出しOnNext(T)はほとんど保護されていないことがわかります。

これをor演算子のIObservable<T>実装と比較してみましょう。これらの拡張メソッドは両方とも、クラスを拡張する実装を返します。これらのオブザーバブル シーケンスの 1 つにサブスクリプションが作成されると、サブスクライブしているオブザーバーが実装によってラップされます。これが重要な違いです。WhereCastIObservable<T>Producer<T>SafeObserver<T>

この実装を調べてみると、コード パスの anon オブザーバーで MakeSafe メソッドが呼び出されることがわかります。これにより、OnNext へのすべての呼び出しが try/finally でラップされるようになりました。

public void OnNext(T value)
{
    if(this.isStopped!=0)
        return;
    bool flag = true;
    try
    {
        this._onNext(value);
        flag=true;        //Flag only set if OnNext doesn't throw!!
    }
    finally
    {
        if(!flag)
            this._disposable.Dispose();
    }
}

上記のように安全なオブザーバーを取得するとOnNext、ハンドラーがスローした場合flag、 は true に設定されず、_disposableインスタンスは破棄されることに注意してください。この場合、_disposableインスタンスはサブスクリプションを表します。

したがって、生Subjectがテストに合格する理由と、一見無害なオペレーターが動作の変化を引き起こす場所についての説明があります。

がデフォルトでこのように動作しない理由についてSubject<T>は、これは 2.0 リリースで行われたパフォーマンスの改善によるものだと思います。サブジェクトは生のパフォーマンスに合わせて調整されていると感じており、それらを使用する勇気がある場合は、自分が何をしているのかを知っている (つまり、OnNextハンドラーを投入していない!) と想定しています。私は、彼らがサブジェクトから並行性の安全性をデフォルトで削除し、拡張メソッドでそれをオンにする必要があるというこの仮定に基づいていますSynchronize()。おそらく、それらの余分な try/finally 呼び出しはすべて、オプトインした場合にのみ支払われるべきだと考えていたのでしょう。これらの安全機能を選択する方法は、上記Where(_=>true)またはより一般的に行ったことを行うことですAsObservable()

于 2013-04-02T09:52:50.900 に答える