次の方法で共有


オブサーバー デザイン パターンのベスト プラクティス

.NET では、オブザーバー デザイン パターンは、一連のインターフェイスとして実装されます。 System.IObservable<T> インターフェイスはデータ プロバイダーを表し、データ プロバイダーはオブザーバーで通知のサブスクリプションを解除できるようにする IDisposable 実装も提供します。 System.IObserver<T> インターフェイスはオブザーバーを表します。 このトピックでは、これらのインターフェイスを使用してオブザーバー デザイン パターンを実装するときに、開発者が適用することが望ましいベスト プラクティスについて説明します。

スレッド

通常、プロバイダーは、何らかのコレクション オブジェクトで表されるサブスクライバー リストに特定のオブザーバーを追加することで、IObservable<T>.Subscribe メソッドを実装し、サブスクライバー リストから特定のオブザーバーを削除することで、IDisposable.Dispose メソッドを実装します。 オブザーバーは、これらのメソッドをいつでも呼び出すことができます。 また、プロバイダー/オブザーバーのコントラクトでは、IObserver<T>.OnCompleted コールバック メソッドの後にだれがサブスクリプションを解除するかが指定されていないため、プロバイダーとオブザーバーの両方で同じメンバーをリストから削除しようとする可能性があります。 このような可能性があるため、Subscribe メソッドと Dispose メソッドはどちらもスレッド セーフである必要があります。 通常、これには、同時実行コレクションまたはロックの使用が必要です。 非スレッド セーフの実装では、スレッド セーフではないことが明示的に記載されている必要があります。

その他の保証は、プロバイダー/オブザーバーのコントラクトの最上部のレイヤーで指定されている必要があります。 実装側で要件を追加する場合には、ユーザーがオブザーバー コントラクトについて混乱しないように明確に示す必要があります。

例外処理

データ プロバイダーとオブザーバーの結合は疎であるため、オブザーバー デザイン パターンの例外は情報提供を目的としています。 このことは、プロバイダーとオブザーバーがオブザーバー デザイン パターンの例外を処理する方法に影響します。

プロバイダー — OnError メソッドの呼び出し

OnError メソッドは、IObserver<T>.OnNext メソッドと同様に、オブザーバーへの情報メッセージとして使用されます。 ただし、OnNext メソッドが、現在のデータまたは更新されたデータをオブザーバーに提供するように設計されているのに対して、OnError メソッドは、プロバイダーが有効なデータを提供できないことを示すように設計されています。

例外を処理して OnError メソッドを呼び出す場合、プロバイダーが次のベスト プラクティスに従うことをお勧めします。

  • プロバイダーに固有の要件がある場合、プロバイダーは独自の例外を処理する必要があります。

  • プロバイダーは、オブザーバーが特定の方法で例外を処理することを期待または要求しないようにします。

  • 更新を提供する機能を損なうような例外を処理する場合、プロバイダーは OnError メソッドを呼び出す必要があります。 このような例外の情報はオブザーバーに渡すことができます。 それ以外の場合は、オブザーバーに例外を通知する必要はありません。

プロバイダーが OnError メソッドまたは IObserver<T>.OnCompleted メソッドを呼び出すと、それ以上の通知は行われなくなるため、プロバイダーはそのオブザーバーのサブスクリプションを解除することができます。 ただし、オブザーバーも、OnError 通知または IObserver<T>.OnCompleted 通知を受信する前と後の両方を含め、いつでも自分自身のサブスクリプションを解除できます。 オブザーバー デザイン パターンでは、プロバイダーとオブザーバーのどちらがサブスクリプションの解除を行うかは指定しません。そのため、両方でサブスクリプションの解除を試みる可能性があります。 通常、オブザーバーは、サブスクリプションを解除すると、サブスクライバーのコレクションから削除されます。 シングルスレッド アプリケーションでは、削除を試みる前に、オブジェクト参照が有効であること、およびオブジェクトがサブスクライバーのコレクションのメンバーであることを、IDisposable.Dispose の実装で確認する必要があります。 マルチスレッド アプリケーションでは、System.Collections.Concurrent.BlockingCollection<T> オブジェクトなどのスレッド セーフなコレクション オブジェクトを使用する必要があります。

オブザーバー — OnError メソッドの実装

プロバイダーからエラー通知を受信した場合、オブザーバーは例外を情報として処理する必要がありますが、特定のアクションの実行は要求されません。

プロバイダーからの OnError メソッド呼び出しに応答する場合、オブザーバーが次のベスト プラクティスに従うことをお勧めします。

  • OnNext または OnError などのインターフェイス実装からオブザーバーが例外をスローすることを回避するようにします。 オブザーバーが例外をスローする場合は、これらの例外が未処理になることを想定する必要があります。

  • 呼び出し履歴を保持するために、Exception メソッドに渡された OnError オブジェクトをスローするオブザーバーは、オブジェクトをスローする前に例外をラップする必要があります。 このためには、標準の例外オブジェクトを使用する必要があります。

その他のベスト プラクティス

IObservable<T>.Subscribe メソッドで登録を解除しようとすると、null 参照になる場合があります。 そのため、この方法は避けることをお勧めします。

1 つのオブザーバーを複数のプロバイダーにアタッチすることは可能ですが、推奨パターンは、IObserver<T> インスタンスを 1 つの IObservable<T> インスタンスにのみアタッチすることです。

参照