在本文中,您将了解 .NET C# 中的观察者设计模式以及一些增强功能。
观察者设计模式是最重要和最常用的设计模式之一。
首先,让我们检查一下观察者设计模式的正式定义。
按照
观察者设计模式使订阅者能够向提供者注册并接收来自提供者的通知。适用于任何需要推送通知的场景。该模式定义了一个提供者(也称为主题或可观察对象)和零个、一个或多个观察者。观察者向提供者注册,每当发生预定义的条件、事件或状态更改时,提供者都会通过调用观察者的方法之一自动通知所有观察者。在此方法调用中,提供者还可以向观察者提供当前状态信息。在 .NET 中,通过实现通用System.IObservable<T>和System.IObserver<T>接口来应用观察者设计模式。通用类型参数表示提供通知信息的类型。
正如我们现在所知, Observer 设计模式制定了Observable和Observer模块之间的关系。观察者设计模式的独特之处在于,使用它您可以在没有紧密耦合关系的情况下实现这一点。
分析模式的工作方式,您会发现以下内容:
这些是用于在.NET C#中实现观察者设计模式的抽象。
这是代表任何Observable 的Covariant接口。如果你想了解更多关于 .NET 中的 Variance,你可以查看这篇文章
该接口中定义的成员有:
public IDisposable Subscribe (IObserver<out T> observer);
应该调用Subscribe
方法来通知Observable某个Observer对其信息流感兴趣。
Subscribe
方法返回一个实现IDisposable
接口的对象。然后Observer可以使用该对象取消订阅Observable提供的信息流。完成此操作后,将不会通知观察者有关信息流的任何更新。
这是一个代表任何Observer 的逆变接口。如果你想了解更多关于 .NET 中的 Variance,你可以查看这篇文章
该接口中定义的成员有:
public void OnCompleted (); public void OnError (Exception error); public void OnNext (T value);
Observable应该调用OnCompleted
方法来通知Observer信息流已经完成并且Observer不应该期待任何更多信息。
Observable应该调用OnError
方法来通知Observer发生了错误。
Observable应该调用OnNext
方法来通知Observer一条新的信息已经准备好并且正在添加到流中。
现在,让我们看看Microsoft如何推荐在 C# 中实现观察者设计模式。稍后,我将向您展示我自己实现的一些小改进。
我们将构建一个简单的天气预报控制台应用程序。在这个应用程序中,我们将拥有WeatherForecast模块(Observable、Provider、Subject)和WeatherForecastObserver模块(Observer)。
那么,让我们开始研究实现。
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>
。Subscribe
方法的实现中,我们检查传入的 Observer 之前是否已经注册过。如果没有,我们将其添加到本地m_Observers
观察者列表中。然后,我们逐一循环本地m_WeatherInfoList
列表中的所有WeatherInfo
条目,并通过调用 Observer 的OnNext
方法通知 Observer。WeatherForecastUnsubscriber
类的新实例,供 Observer 用于取消订阅信息流。RegisterWeatherInfo
方法,以便主模块可以注册新的WeatherInfo
。在现实世界中,这可以替换为内部计划的API 调用或SignalR Hub的侦听器或其他可以充当信息源的东西。
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
。
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
方法中,我们将温度写入控制台。OnCompleted
方法中,我们将“Completed”写入控制台。OnError
方法中,我们将“Error”写入控制台。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(); } } }
我们可以在这里注意到:
当我检查微软的实现时,我发现了一些问题。因此,我决定做一些小改动。
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>
接口。IReadOnlyCollection<T> Snapshot
属性,以允许其他模块无需订阅即可获取现有信息条目的即时列表。bool withHistory
参数定义了IDisposable Subscribe(IObserver<T> observer, bool withHistory)
方法,以便 Observer 可以决定是否要在订阅时收到有关已存在信息条目的通知。
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
类不是通用的。
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>
接口之上构建的库提供了更多您可能会觉得有用的很酷的特性和功能。
这些库中的一个是
因此,我鼓励您探索这些库并尝试一下。我相信你会喜欢其中的一些。
也发布在这里。