C#面试题更新 2026-08-03

在 C# 中,观察者模式可以通过 IObservable 和 IObserver 接口来实现。请解释这两个接口各自承担的角色,并说明一个典型的实现步骤:包括定义观察者、订阅机制、通知流程以及如何取消订阅。同时,请指出使用这两个接口与直接自定义委托或事件方式相比,在异步推送或多线程场景下的优势或注意事项。

考察说明

考查对 C# 中 IObservable/IObserver 接口的理解及其在观察者模式中的具体实现与特性。

回答思路

  1. 【回答框架 1】IObservable<T> 和 IObserver<T> 是 .NET 标准接口,用于提供基于推送的通知机制。IObservable<T> 代表可观察对象,其核心方法是 Subscribe(IObserver<T> observer),返回 IDisposable 用于取消订阅;IObserver<T> 代表观察者,包含 OnNext、OnError 和 OnCompleted 三个方法,分别处理数据推送、异常和通知结束。
  2. 【回答框架 2】实现步骤:首先定义具体观察者类实现 IObserver<T>,在 OnNext 中处理数据,OnError 中处理异常,OnCompleted 中做清理;然后定义可观察类实现 IObservable<T>,内部维护订阅者列表,Subscribe 方法需要将观察者加入列表并返回一个 IDisposable,其 Dispose 方法用于从列表移除;最后在数据产生时遍历列表调用各观察者的 OnNext。
  3. 【回答框架 3】相比使用事件或委托,IObservable/IObserver 更规范,且天然支持异步推送(如在 Task 中生产数据)和错误处理(通过 OnError 传递异常)。但需要注意线程安全:多线程环境下的订阅、通知和取消订阅可能产生竞态条件,通常需要锁或使用同步集合。
  4. 【回答框架 4】此外,IObservable 接口本身不提供内置的线程模型,实现时需自行保证线程安全,例如在通知时对订阅者列表加锁。也可借助 System.Reactive(Rx.NET)库中的实现,它提供了丰富的 LINQ 操作符来组合和处理数据流,但需注意该库并非 .NET 标准库的一部分。
  5. 【回答框架 5】关键点:订阅方法的返回值必须实现 IDisposable,且 Dispose 需是幂等的;在通知过程中如果有观察者取消订阅,应避免修改正在遍历的集合。
  6. 【关键点 1】IObservable<T> 是数据源,IObserver<T> 是订阅者,Subscribe 返回 IDisposable 用于取消订阅。
  7. 【关键点 2】Observe 模式支持推送通知,通过 OnNext 推送数据,OnError 传递异常,OnCompleted 通知结束。
  8. 【关键点 3】实现时需处理线程安全问题,可使用锁或同步集合保证订阅和通知的原子性。
  9. 【关键点 4】取消订阅应在 Dispose 中实现,且需避免在遍历时修改订阅列表。
  10. 【易错点 1】若在 OnNext 中调用观察者的取消订阅方法,可能引发集合修改异常,应使用迭代器副本或延迟移除。
  11. 【易错点 2】IObservable 接口不保证线程安全,未加锁时多线程同时订阅或通知会导致数据竞争。
  12. 【易错点 3】误以为 Subscribe 方法必须立即传递所有已有数据,实际上默认是推送后续新数据,除非结合其他机制(如 ReplaySubject)。