私にとっての本当の疑問は、なぜ 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 つにサブスクリプションが作成されると、サブスクライブしているオブザーバーが実装によってラップされます。これが重要な違いです。Where
Cast
IObservable<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()
。