C#怎么实现观察者模式 C#设计模式之观察者模式教程

来源:这里教程网 时间:2026-02-21 17:41:28 作者:

在C#中实现观察者模式,核心是定义“被观察者(Subject)”和“观察者(Observer)”,让多个观察者能动态订阅、接收被观察者状态变化的通知。.NET 原生提供了

IObservable<t></t>
IObserver<t></t>
接口,是最轻量、标准且推荐的方式——无需手写事件委托或自定义接口,也天然支持取消订阅和异常传递。

用 IObservable/IObserver 实现标准观察者

这是最符合 .NET 理念的写法,语义清晰、线程安全基础好、可组合性强(配合 System.Reactive 可进一步增强)。

被观察者需实现
IObservable<t></t>
,通常返回一个包装了订阅逻辑的对象(如
Observable.Create<t>()</t>
观察者实现
IObserver<t></t>
,重写
OnNext
(接收数据)、
OnError
(处理异常)、
OnCompleted
(通知结束)
调用
Subscribe()
建立连接;返回的
IDisposable
可用于主动取消订阅

示例:温度传感器模拟

// 被观察者:温度数据源
var temperatureSource = Observable.Create<double>(observer =>
{
    var timer = new Timer(_ =>
    {
        var temp = 20 + new Random().NextDouble() * 10;
        observer.OnNext(temp);
    }, null, TimeSpan.Zero, TimeSpan.FromSeconds(2));
<pre class="brush:php;toolbar:false;">return () => timer.Dispose(); // 取消时释放资源

});

// 观察者:控制台显示 var subscription = temperatureSource.Subscribe( temp => Console.WriteLine($"当前温度:{temp:F2}°C"), ex => Console.WriteLine($"错误:{ex.Message}"), () => Console.WriteLine("传感器已关闭") );

// 5秒后取消订阅 await Task.Delay(5000); subscription.Dispose(); // 主动断开,timer 也会被释放

用事件(event)+ 自定义委托实现简易版

适合入门理解或轻量场景,代码直观,但需手动管理订阅列表、线程安全、异常隔离等细节。

在被观察者类中声明
public event EventHandler<temperatureeventargs> TemperatureChanged;</temperatureeventargs>
定义事件参数类继承
EventArgs
,携带通知数据(如温度值)
触发时调用
TemperatureChanged?.Invoke(this, new TemperatureEventArgs(temp));
观察者通过
sensor.TemperatureChanged += OnTemperatureChanged;
订阅

注意:多线程下需加锁或使用

EventHandlerList
;事件不提供取消订阅外的生命周期控制。

用泛型委托 Action 搭配 List 存储观察者

比事件更灵活,便于批量操作或条件过滤,但失去事件的封装性和编译时安全(比如不能用

-=
安全移除匿名方法)。

被观察者维护
private readonly List<action>> _observers = new();</action>
提供
Subscribe(Action<double> action)</double>
Unsubscribe(Action<double> action)</double>
状态变更时遍历调用每个
action(temp)
;建议捕获异常避免一个失败影响其他观察者

关键注意事项

无论哪种实现,都要重视三点:

内存泄漏风险:事件或委托长期持有观察者引用,导致无法 GC;务必提供明确的取消机制并及时调用 线程安全:若通知可能跨线程触发(如定时器、Task),需确保
Subscribe
/
Unsubscribe
和通知过程线程安全(可用
ConcurrentBag
或锁)
异常隔离:一个观察者出错不应中断其他观察者的通知;标准
IObserver
OnError
已做分离,手写方案需 try/catch 包裹每个调用

基本上就这些。用

IObservable/IObserver
是现代 C# 的首选,简洁、健壮、可扩展;事件适合快速原型;自定义委托适合教学或特殊调度需求。

相关推荐