在本文中,您将了解 .NET C# 中的观察者设计模式以及一些增强功能。 定义 观察者设计模式 是最重要和最常用的设计模式之一。 观察者设计模式 首先,让我们检查一下 的正式定义。 观察者设计模式 按照 : 微软的文档 观察者设计模式使订阅者能够向提供者注册并接收来自提供者的通知。适用于任何需要推送通知的场景。该模式定义了一个提供者(也称为主题或可观察对象)和零个、一个或多个观察者。观察者向提供者注册,每当发生预定义的条件、事件或状态更改时,提供者都会通过调用观察者的方法之一自动通知所有观察者。在此方法调用中,提供者还可以向观察者提供当前状态信息。在 .NET 中,通过实现通用 和 接口来应用观察者设计模式。通用类型参数表示提供通知信息的类型。 System.IObservable<T> System.IObserver<T> 因此,从上面的定义,我们可以理解以下内容: 我们有两个派对或模块。 提供一些信息流的模块。这个模块被称为 (因为它提供信息),或 (因为它将信息提供给外界),或 (因为它可以被外界观察到)。 Provider Subject Observable 对来自其他地方的信息流感兴趣的模块。这个模块被称为 (因为它观察信息)。 观察者 观察者设计模式的优点 正如我们现在所知, 制定了 和 模块之间的关系。 独特之处在于,使用它您可以在没有紧密耦合关系的情况下实现这一点。 Observer 设计模式 Observable Observer 观察者设计模式的 分析模式的工作方式,您会发现以下内容: 知道关于 所需的最少信息。 Observable Observer 知道关于 所需的最少信息。 Observer Observable 甚至相互知识也是通过抽象实现的,而不是具体的实现。 最后,两个模块都可以完成他们的工作,而且只能完成他们的工作。 使用的抽象 这些是用于在 中实现 。 .NET C# 观察者设计模式的 抽象 IObservable<输出T> 这是代表任何 接口。如果你想了解更多关于 .NET 中的 Variance,你可以查看这篇文章 . Observable 的 Covariant .NET C# 中的协变和逆变 该接口中定义的成员有: public IDisposable Subscribe (IObserver<out T> observer); 应该调用 方法来通知 某个 对其信息流感兴趣。 Subscribe Observable Observer 方法返回一个实现 接口的对象。然后 可以使用该对象取消订阅 提供的信息流。完成此操作后,将不会通知 有关信息流的任何更新。 Subscribe IDisposable Observer Observable 观察者 IObserver<in T> 这是一个代表任何 接口。如果你想了解更多关于 .NET 中的 Variance,你可以查看这篇文章 . Observer 的 逆变 .NET C# 中的协变和逆变 该接口中定义的成员有: public void OnCompleted (); public void OnError (Exception error); public void OnNext (T value); 应该调用 方法来通知 信息流已经完成并且 不应该期待任何更多信息。 Observable OnCompleted Observer Observer 应该调用 方法来通知 发生了错误。 Observable OnError Observer 应该调用 方法来通知 一条新的信息已经准备好并且正在添加到流中。 Observable OnNext Observer 微软的实施 现在,让我们看看 如何推荐在 C# 中实现 。稍后,我将向您展示我自己实现的一些小改进。 Microsoft 观察者设计模式 我们将构建一个简单的 。在这个应用程序中,我们将拥有 模块(Observable、Provider、Subject)和 模块(Observer)。 天气预报控制台应用程序 WeatherForecast WeatherForecastObserver 那么,让我们开始研究实现。 天气资讯 namespace Observable { public class WeatherInfo { internal WeatherInfo(double temperature) { Temperature = temperature; } public double Temperature { get; } } } 这是表示要在信息流中流动的信息片段的实体。 天气预报 using System; using System.Collections.Generic; namespace Observable { public class WeatherForecast : IObservable<WeatherInfo> { private readonly List<IObserver<WeatherInfo>> m_Observers; private readonly List<WeatherInfo> m_WeatherInfoList; public WeatherForecast() { m_Observers = new List<IObserver<WeatherInfo>>(); m_WeatherInfoList = new List<WeatherInfo>(); } public IDisposable Subscribe(IObserver<WeatherInfo> observer) { if (!m_Observers.Contains(observer)) { m_Observers.Add(observer); foreach (var item in m_WeatherInfoList) { observer.OnNext(item); } } return new WeatherForecastUnsubscriber(m_Observers, observer); } public void RegisterWeatherInfo(WeatherInfo weatherInfo) { m_WeatherInfoList.Add(weatherInfo); foreach (var observer in m_Observers) { observer.OnNext(weatherInfo); } } public void ClearWeatherInfo() { m_WeatherInfoList.Clear(); } } } 我们可以在这里注意到: 类正在实现 。 WeatherForecast IObservable<WeatherInfo> 在 方法的实现中,我们检查传入的 Observer 之前是否已经注册过。如果没有,我们将其添加到本地 观察者列表中。然后,我们逐一循环本地 列表中的所有 条目,并通过调用 Observer 的 方法通知 Observer。 Subscribe m_Observers m_WeatherInfoList WeatherInfo OnNext 最后,我们返回一个 类的新实例,供 Observer 用于取消订阅信息流。 WeatherForecastUnsubscriber 定义了 方法,以便主模块可以注册新的 。在现实世界中,这可以替换为内部计划的 或 的侦听器或其他可以充当信息源的东西。 RegisterWeatherInfo WeatherInfo API 调用 SignalR Hub 退订者<T> using System; using System.Collections.Generic; namespace Observable { public class Unsubscriber<T> : IDisposable { private readonly List<IObserver<T>> m_Observers; private readonly IObserver<T> m_Observer; private bool m_IsDisposed; public Unsubscriber(List<IObserver<T>> observers, IObserver<T> observer) { m_Observers = observers; m_Observer = observer; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (m_IsDisposed) return; if (disposing && m_Observers.Contains(m_Observer)) { m_Observers.Remove(m_Observer); } m_IsDisposed = true; } ~Unsubscriber() { Dispose(false); } } } 我们可以在这里注意到: 这是任何取消订阅者的基类。 它通过应用 来实现 。 一次性设计模式 IDisposable 通过构造函数,它接收完整的 Observer 列表以及为其创建的 Observer。 在处理时,它会检查观察者是否已经存在于观察者的完整列表中。如果是,则将其从列表中删除。 天气预报退订 using System; using System.Collections.Generic; namespace Observable { public class WeatherForecastUnsubscriber : Unsubscriber<WeatherInfo> { public WeatherForecastUnsubscriber( List<IObserver<WeatherInfo>> observers, IObserver<WeatherInfo> observer) : base(observers, observer) { } } } 我们可以在这里注意到: 这是从 类继承的。 Unsubscriber<T> 没有特殊处理发生。 天气预报观察员 using System; namespace Observable { public class WeatherForecastObserver : IObserver<WeatherInfo> { private IDisposable m_Unsubscriber; public virtual void Subscribe(WeatherForecast provider) { m_Unsubscriber = provider.Subscribe(this); } public virtual void Unsubscribe() { m_Unsubscriber.Dispose(); } public void OnCompleted() { Console.WriteLine("Completed"); } public void OnError(Exception error) { Console.WriteLine("Error"); } public void OnNext(WeatherInfo value) { Console.WriteLine($"Temperature: {value.Temperature}"); } } } 我们可以在这里注意到: 类正在实现 。 WeatherForecastObserver IObserver<WeatherInfo> 在 方法中,我们将温度写入控制台。 OnNext 在 方法中,我们将“Completed”写入控制台。 OnCompleted 在 方法中,我们将“Error”写入控制台。 OnError 我们定义了 方法以允许主模块触发注册过程。返回的取消订阅者对象在内部保存,以备取消订阅时使用。 void Subscribe(WeatherForecast provider) 使用相同的概念,定义了 方法,它使用内部保存的取消订阅者对象。 void Unsubscribe() 程序 using System; namespace Observable { class Program { static void Main(string[] args) { var provider = new WeatherForecast(); provider.RegisterWeatherInfo(new WeatherInfo(1)); provider.RegisterWeatherInfo(new WeatherInfo(2)); provider.RegisterWeatherInfo(new WeatherInfo(3)); var observer = new WeatherForecastObserver(); observer.Subscribe(provider); provider.RegisterWeatherInfo(new WeatherInfo(4)); provider.RegisterWeatherInfo(new WeatherInfo(5)); observer.Unsubscribe(); provider.RegisterWeatherInfo(new WeatherInfo(6)); observer.Subscribe(provider); provider.RegisterWeatherInfo(new WeatherInfo(7)); Console.ReadLine(); } } } 我们可以在这里注意到: 我们创建了提供者的一个实例。 然后注册了3条信息。 到目前为止,由于没有定义任何观察者,因此不应将任何内容记录到控制台。 然后创建了一个观察者的实例。 然后订阅观察者流。 此时,我们应该在控制台中找到 3 个记录的温度。这是因为当观察者订阅时,它会收到有关已经存在的信息的通知,在我们的例子中,它们是 3 条信息。 然后我们注册2条信息。 因此,我们在控制台中记录了另外 2 条消息。 然后我们退订。 然后我们注册 1 条信息。 但是,这条信息不会记录到控制台,因为观察者已经取消订阅。 然后观察者再次订阅。 然后我们注册 1 条信息。 因此,这条信息被记录到控制台。 最后,运行它应该以这个结果结束: 我的扩展实现 当我检查微软的实现时,我发现了一些问题。因此,我决定做一些小改动。 IExtendedObservable<输出 T> using System; using System.Collections.Generic; namespace ExtendedObservable { public interface IExtendedObservable<out T> : IObservable<T> { IReadOnlyCollection<T> Snapshot { get; } IDisposable Subscribe(IObserver<T> observer, bool withHistory); } } 我们可以在这里注意到: 接口扩展了 接口。 IExtendedObservable<out T> IObservable<T> 它是 。如果您想了解更多相关信息,可以查看文章 . 协变的 .NET C# 中的协变和逆变 我们定义了 属性,以允许其他模块无需订阅即可获取现有信息条目的即时列表。 IReadOnlyCollection<T> Snapshot 我们还使用额外的 参数定义了 方法,以便 Observer 可以决定是否要在订阅时收到有关已存在信息条目的通知。 bool withHistory IDisposable Subscribe(IObserver<T> observer, bool withHistory) 退订 using System; namespace ExtendedObservable { public class Unsubscriber : IDisposable { private readonly Action m_UnsubscribeAction; private bool m_IsDisposed; public Unsubscriber(Action unsubscribeAction) { m_UnsubscribeAction = unsubscribeAction; } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (m_IsDisposed) return; if (disposing) { m_UnsubscribeAction(); } m_IsDisposed = true; } ~Unsubscriber() { Dispose(false); } } } 我们可以在这里注意到: 现在, 类不是通用的。 Unsubscriber 这是因为它不再需要知道信息实体的类型。 它没有访问完整的观察者列表和它为其创建的观察者,而是在它被处置时通知 Observable 并且 Observable 自己处理注销过程。 这样,它做的比以前少,它只是在做它的工作。 天气预报退订 using System; using System.Collections.Generic; namespace ExtendedObservable { public class WeatherForecastUnsubscriber : Unsubscriber { public WeatherForecastUnsubscriber( Action unsubscribeAction) : base(unsubscribeAction) { } } } 我们可以在这里注意到: 我们从 中删除了 部分。 Unsubscriber<T> <T> 现在构造函数接收一个 以在处理时调用。 Action 天气预报 using System; using System.Collections.Generic; namespace ExtendedObservable { public class WeatherForecast : IExtendedObservable<WeatherInfo> { private readonly List<IObserver<WeatherInfo>> m_Observers; private readonly List<WeatherInfo> m_WeatherInfoList; public WeatherForecast() { m_Observers = new List<IObserver<WeatherInfo>>(); m_WeatherInfoList = new List<WeatherInfo>(); } public IReadOnlyCollection<WeatherInfo> Snapshot => m_WeatherInfoList; public IDisposable Subscribe(IObserver<WeatherInfo> observer) { return Subscribe(observer, false); } public IDisposable Subscribe(IObserver<WeatherInfo> observer, bool withHistory) { if (!m_Observers.Contains(observer)) { m_Observers.Add(observer); if (withHistory) { foreach (var item in m_WeatherInfoList) { observer.OnNext(item); } } } return new WeatherForecastUnsubscriber( () => { if (m_Observers.Contains(observer)) { m_Observers.Remove(observer); } }); } public void RegisterWeatherInfo(WeatherInfo weatherInfo) { m_WeatherInfoList.Add(weatherInfo); foreach (var observer in m_Observers) { observer.OnNext(weatherInfo); } } public void ClearWeatherInfo() { m_WeatherInfoList.Clear(); } } } 我们可以在这里注意到: 它几乎相同,除了 属性返回内部 列表但作为 。 IReadOnlyCollection<WeatherInfo> Snapshot m_WeatherInfoList IReadOnlyCollection 方法使用了 参数。 IDisposable Subscribe(IObserver<WeatherInfo> observer, bool withHistory) withHistory 天气预报观察员 using System; namespace ExtendedObservable { public class WeatherForecastObserver : IObserver<WeatherInfo> { private IDisposable m_Unsubscriber; public virtual void Subscribe(WeatherForecast provider) { m_Unsubscriber = provider.Subscribe(this, true); } public virtual void Unsubscribe() { m_Unsubscriber.Dispose(); } public void OnCompleted() { Console.WriteLine("Completed"); } public void OnError(Exception error) { Console.WriteLine("Error"); } public void OnNext(WeatherInfo value) { Console.WriteLine($"Temperature: {value.Temperature}"); } } } 我们在这里可以注意到,它几乎是相同的,除了 现在决定是否应该 历史。 Subscribe(WeatherForecast provider) Subscribe 程序 using System; namespace ExtendedObservable { class Program { static void Main(string[] args) { var provider = new WeatherForecast(); provider.RegisterWeatherInfo(new WeatherInfo(1)); provider.RegisterWeatherInfo(new WeatherInfo(2)); provider.RegisterWeatherInfo(new WeatherInfo(3)); var observer = new WeatherForecastObserver(); observer.Subscribe(provider); provider.RegisterWeatherInfo(new WeatherInfo(4)); provider.RegisterWeatherInfo(new WeatherInfo(5)); observer.Unsubscribe(); provider.RegisterWeatherInfo(new WeatherInfo(6)); observer.Subscribe(provider); provider.RegisterWeatherInfo(new WeatherInfo(7)); Console.ReadLine(); } } } 和以前一样。 最后,运行它应该会得到与之前相同的结果: 下一步是什么 现在,您了解了 .NET C# 中 的基础知识。然而,这并不是故事的结局。 观察者设计模式 在 和 接口之上构建的库提供了更多您可能会觉得有用的很酷的特性和功能。 IObservable<T> IObserver<T> 这些库中的一个是 图书馆。它由一组扩展方法和 LINQ 标准序列运算符组成,以支持异步编程。 .NET (Rx) 的响应式扩展 因此,我鼓励您探索这些库并尝试一下。我相信你会喜欢其中的一些。 也发布 在这里。