观察程序设计模式最佳做法

在 .NET 中,观察程序设计模式作为一组接口实现。 接口 System.IObservable<T> 表示数据提供程序,该提供程序还负责提供允许 IDisposable 观察者取消订阅通知的实现。 接口 System.IObserver<T> 表示观察者。 本主题介绍开发人员使用这些接口实现观察者设计模式时应遵循的最佳做法。

线程

通常,提供程序通过将特定观察程序添加到由某些集合对象表示的订阅服务器列表来实现 IObservable<T>.Subscribe 该方法,并通过从订阅者列表中删除特定观察程序来实现 IDisposable.Dispose 该方法。 观察者可以随时调用这些方法。 此外,由于提供程序/观察者合同并未明确规定谁应在回调方法 IObserver<T>.OnCompleted 后负责取消订阅,因此提供程序和观察者可能会同时尝试从列表中删除同一成员。 由于这种可能性,SubscribeDispose 两种方法都应是线程安全的。 通常,这涉及到使用 并发集合 或锁。 非线程安全的实现应显式注明它们非线程安全。

在提供程序/观察者协议的基础层之上,必须具体说明任何额外的保证。 实施者应在实施其他要求时明确标注,以避免用户对观察者合同造成混淆。

处理异常

由于数据提供程序与观察者之间的松散耦合,观察者设计模式中的异常旨在提供信息性。 这会影响提供程序和观察程序如何处理观察程序设计模式中的异常。

提供者 - 调用 OnError 方法

OnError 方法旨在作为向观察者传达信息的信息性消息,这与 IObserver<T>.OnNext 方法非常类似。 但是, OnNext 该方法旨在向观察者提供当前或更新的数据,而 OnError 该方法旨在指示提供程序无法提供有效数据。

提供程序在处理异常和调用 OnError 方法时应遵循以下最佳做法:

  • 如果提供程序有任何具体需求,则它必须处理自己的异常。

  • 提供方不应期望或要求观察者以任何特定的方法处理异常。

  • 当提供程序处理破坏其提供更新能力的异常时,提供程序应调用 OnError 该方法。 有关此类异常的信息可以传递给观察者。 在其他情况下,无需通知观察者异常情况。

提供程序调用 OnErrorIObserver<T>.OnCompleted 方法后,不应再有任何通知,并且提供程序可以取消其观察者的订阅。 但是,观察者也可以随时取消订阅,包括在接收OnErrorIObserver<T>.OnCompleted通知的前后。 观察程序设计模式不决定提供程序还是观察者负责取消订阅;因此,两者都可能尝试取消订阅。 通常,当观察者取消订阅时,它们将从订阅者集合中删除。 在单线程应用程序中, IDisposable.Dispose 实现应确保对象引用有效,并且该对象是订阅服务器集合的成员,然后再尝试删除它。 在多线程应用程序中,应使用线程安全的集合对象(如 System.Collections.Concurrent.BlockingCollection<T> 对象)。

观察者 - 实现 OnError 方法

当观察者收到来自提供程序的错误通知时,观察者应将异常视为信息性,并且不需要执行任何特定操作。

观察者在响应来自提供程序的OnError方法调用时,应遵循以下最佳做法:

  • 观察者不应从其接口实现(如 OnNextOnError)中抛出异常。 但是,如果观察者确实抛出异常,它应当预期这些异常将不被处理。

  • 若要保留调用堆栈,希望抛出传递给其 Exception 方法的 OnError 对象的观察者,应在抛出之前将异常封装。 应将标准异常对象用于此目的。

其他最佳做法

尝试在 IObservable<T>.Subscribe 方法中取消注册可能会导致空引用。 因此,建议避免这种做法。

尽管可以将观察者附加到多个提供程序,但建议的模式是仅将IObserver<T>实例附加到一个IObservable<T>实例。

另请参阅