C#观察者模式应用

news2025/1/10 20:22:34

目录

一、什么是观察者模式

二、C#中观察者模式的实现

三、两种实现的用法

1、事件与委托

2、IObserver和IObservable

四、参考文献


一、什么是观察者模式

观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式(Publish-Subscribe)、模型-视图(Model-View)模式、源监听器(Source-Listener)或者从属者模式,它是对象行为型模式。

  • 模型(Model) -发布者是数据源,模型层, 程序员编写程序应有的功能(实现算法等等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能)。
  • 视图(View) -监听者是视图层, 界面设计人员进行图形界面设计。

观察者模式是一种对象行为型模式,其主要优点如下。

  1. 降低了目标与观察者之间的耦合关系,两者之间是抽象耦合关系。符合依赖倒置原则。
  2. 目标与观察者之间建立了一套触发机制。

它的主要缺点如下。

  1. 目标与观察者之间的依赖关系并没有完全解除,而且有可能出现循环引用。
  2. 当观察者对象很多时,通知的发布会花费很多时间,影响程序的效率。

二、C#中观察者模式的实现

C#语言,其事件和委托本身就是观察者模式的基本实现。除此之外,属性修改通知以及属性依赖等也是观察者模式的用途之一,在WinForm或者WPF中,通常将集合类控件,绑定到集合上,当集合数据发生变化时,绑定的控件能够得到通知,并且能够自动刷新界面。

    在C#中使用观察者模式,除了常用的event事件之外,还可以通过IObservable<T>和IObserver<T>两个接口来实现事件流模式,看起来有些复杂,但是这种方式有很多优点,并且很容易能跟Reactive Extensions框架配合。最后讲述了Observerable集合BindingList<T>和ObservableCollection<T>,他们不是线程安全的,使用的时候需要注意多线程读写的问题,这两个集合通常跟配套的支持集合的控件来绑定使用,这样能做到当集合数据发生变化时,对应的界面能够自动刷新。

在软件世界也是这样,例如,Excel 中的数据与折线图、饼状图、柱状图之间的关系;MVC 模式中的模型与视图的关系;事件模型中的事件源与事件处理者。所有这些,如果用观察者模式来实现就非常方便。

C#的设计者通过关键字event来简化对观察者模式的使用。它的基本用法是,首先使用event关键字定义事件,然后注册事件回调方法EventHandler,回调方法通常包含两个参数,一个object类型的sender和一个继承自EventArgs的参数,该参数携带一些触发事件的必要信息。

三、两种实现的用法

1、事件与委托

    event事件其实是对委托的包装。对event的包装成为EventHandler,有泛型和非泛型版本,泛型主要是继承自EventArgs的类型。具体使用可以了解如何使用event定义事件。并且由此了解Action Func的用法,加深对委托的理解。

2、IObserver<T>和IObservable<T>

  如果将一个List<T>绑定到WinForm或者WPF中的ListBox,当List发送改变的时候,UI不会更新,这是因为List<T>并不支持Observer模式。当然如果单个对象发生改变,但整个List并没有事件能够向外通知其内容发送了改变。诚然我们可以对List进行包装,在Add或者Remove方法里添加事件通知,可以但没必要,在WinForm和WPF中都有Observable集合,他们分别是BindingList<T>和ObservableCollection<T>。

    这两个集合类型表现起来就跟Collection<T>一样,但是他提供了额外的通知。比如,对于UI组件,当绑定的集合发送改变时,UI会自动进行更新。ObservableCollection<T>实现了INotifyCollectionChanged接口,所以他有CollectionChanged事件。这个时间会告知集合发生了什么改变,并且会提供旧的和新的元素,以及旧的和新的元素在集合中的位置,换句话说,根据事件我们能够正确的重新绘制ListBox或者其他集合组件。

    需要注意的是BindingList<T>和ObservableCollection<T>都不是线程安全的集合,因此在多线程读写集合的时候,需要特别注意。一种方式是继承自Observable集合,然后对一些写入操作加锁。另外一种方法是继承自ConcurrentBag<T>,然后实现INotifyCollectionChanged接口。相对而言,第一种方式简单一些。

IObservable<T>和IObserver<T> 类型的代码

Weather weather = new Weather() {  Teapreture=new WeatherData { temperature="25度"} };
Person wanglin = new Person() { Name="王玲"};
Person mulan = new Person() { Name = "木兰" };
weather.Subscribe(wanglin);///Subscribe 等同于 事件的"+"
weather.Subscribe(mulan);//
weather.NotifyTeapreture();//公告温度
Console.Read();
/// <summary>
/// 订阅者
/// </summary>
public class Person : IObserver<WeatherData>
{//关注温度
    public string Name { get; set; }
    public void OnCompleted()
    {
        Console.WriteLine(" ");
    }

    public void OnError(Exception error)
    {
        throw new NotImplementedException();
    }

    public void OnNext(WeatherData value)
    {
        Console.WriteLine($"我{Name}知道今天的天气了,是{value.temperature}");

    }
}
/// <summary>
/// 数据
/// </summary>
public class WeatherData
{
    /// <summary>
    /// 气温
    /// </summary>
    public string temperature { get; set; }
    /// <summary>
    /// 湿度
    /// </summary>
    public string humility { get; set; }
    /// <summary>
    /// 气压
    /// </summary>
    public string pressure { get; set; }
}
/// <summary>
/// 发布者
/// </summary>
public class Weather : IObservable<WeatherData>
{
    //提供温度 数据
    public WeatherData Teapreture { get; set; }
    private List<IObserver<WeatherData>> subsribers = new List<IObserver<WeatherData>>();
    public IDisposable Subscribe(IObserver<WeatherData> observer)
    {
        if (!subsribers.Contains(observer))
        {
            subsribers.Add(observer);

        }
        return new UnSubsribe(this, observer);
    }
    class UnSubsribe : IDisposable
    {
        private Weather weather;
        private IObserver<WeatherData> observer;

        public UnSubsribe(Weather weather, IObserver<WeatherData> observer)
        {
            this.weather = weather;
            this.observer = observer;
        }

        public void Dispose()
        {
            weather.subsribers.Remove(observer);
        }
    }
    public void NotifyTeapreture()
    {
        foreach (var item in subsribers)
        {
            item.OnNext(Teapreture);
        }
    }
    public void OnComplete()
    {
        foreach (var item in subsribers)
        {
            item.OnCompleted();
        }
    }

}

四、参考文献

【C# 设计模式】观察者模式 - 小林野夫 - 博客园 (cnblogs.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1836541.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嵌入式中间件_3.嵌入式中间件的一般架构

根据嵌入式中间件的不同类型和其应用对象的不同&#xff0c;其架构也有所不同&#xff0c;通常嵌入式中间件没有统一的架构&#xff0c;这里仅仅列举两种中间件架构。 1.消息中间件 1.1消息中间件原理架构 消息中间件是消息传输过程中保存消息的一种容器。它将消息从它的源中…

Swift开发——索引器扩展

扩展用于向已存在的类型(例如,类、结构体、枚举和协议等)中添加新的功能,扩展甚至可以向系统类型(包括无法查阅代码的类型)中添加新的功能,但是扩展不能覆盖原类型中已有的方法,扩展也不能向类中添加新的存储属性。 01、索引器扩展 扩展可为类、结构体等类型添加索引器。程序段…

【热门开源项目推荐】满足不同程序员的需求与关注点

目录 前言一、热门开源项目介绍二、使用开源热门项目的优势&#xff08;一&#xff09;经济方面&#xff08;二&#xff09;技术方面&#xff08;三&#xff09;社区支持及协作方面 三、程序员选择项目模型建议&#xff08;一&#xff09;关键步骤&#xff08;二&#xff09;示…

技术点梳理0618

ann建库&#xff0c;分布式建库&#xff0c;性能优化&#xff0c;precision recall参数优化 hnsw&#xff0c;图索引 1. build a&#xff09;确定层&#xff1a;类似跳表思路建立多层&#xff0c;对每一个插入的节点&#xff0c;random层号l&#xff0c;从图的起始点search_…

【CVPR2021】LoFTR:基于Transformers的无探测器的局部特征匹配方法

LoFTR&#xff1a;基于Transformers的局部检测器 0. 摘要 我们提出了一种新的局部图像特征匹配方法。我们建议先在粗略级别建立像素级密集匹配&#xff0c;然后再在精细级别细化良好匹配&#xff0c;而不是按顺序进行图像特征检测、描述和匹配。与使用成本体积搜索对应关系的密…

vcruntime140_1.dll文件【安装包】【压缩包】【文件】【下载】

安装程序时有时候出现 类似无法启动程序&#xff0c;缺少vcruntime140_1.dll的提示&#xff0c;我们找到该文件并放到对应目录就可以&#xff1b;获取方法有很多&#xff0c;下面介绍两种&#xff1a;&#xff08;方法二更简便&#xff0c;不过建议两种方法都试试&#xff09; …

Java开发的构建神器:Maven以及如何安装部署Maven

目录 一、Maven引言1.1 Maven的核心概念✍. POM (Project Object Model)✌. 依赖管理✍. 生命周期与构建阶段✌. 插件系统 1.2 Maven的工作流程✍. 读取POM文件&#xff1a;✌. 依赖解析&#xff1a;✍. 构建生命周期&#xff1a;✌. 插件执行&#xff1a;✍. 构建输出&#xf…

shell编程中的运算符的讲解

在Linux操作系统中也可以使用expr来进行一些数值的运算&#xff0c;expr接受表达式作为参数&#xff0c;并打印计算结果。 对于某些复杂的表达式或早期不支持内嵌算术表达式的Shell环境&#xff0c;expr 仍然是一个可行的选择。 如上图所示&#xff0c;是使用变量sum来承接加和…

基于minhook的Windows HOOK

MinHook是一个基于微软Detours技术的可移植Hook库&#xff0c;它允许开发者在运行时更改函数定义&#xff0c;而无需修改原始函数代码。以下是关于MinHook的详细介绍&#xff1a; 基本概念 定义&#xff1a;MinHook使用内存污染和跳转技术来实现Hook&#xff0c;使得开发者能…

【windows|002】WEB服务和域名介绍

&#x1f341;博主简介&#xff1a; &#x1f3c5;云计算领域优质创作者 &#x1f3c5;2022年CSDN新星计划python赛道第一名 &#x1f3c5;2022年CSDN原力计划优质作者 &#x1f3c5;阿里云ACE认证高级工程师 &#x1f3c5;阿里云开发者社区专…

【记录46】【案例】echarts 柱状图

echarts环境4.1.0 <template><div id"threefour"></div> </template> <script> import * as echarts from "echarts" export default {name:"",components:{},data(){return {}},methods:{getdata(){var myChart…

【Java】解决Java报错:SocketTimeoutException during Network Communication

文章目录 引言一、SocketTimeoutException的定义与概述1. 什么是SocketTimeoutException&#xff1f;2. SocketTimeoutException的常见触发场景3. 示例代码 二、解决方案1. 合理设置超时时间2. 使用重试机制3. 使用NIO和异步通信4. 使用高层次的网络通信库 三、最佳实践1. 合理…

使用vscode插件du-i18n处理前端项目国际化翻译多语言

前段时间我写了一篇关于项目国际化使用I18n组件的文章&#xff0c;Vue3 TS 使用国际化组件I18n&#xff0c;那个时候还没真正在项目中使用&#xff0c;需求排期还没有定&#xff0c;相当于是预研。 当时就看了一下大概怎么用&#xff0c;改了一个简单的页面&#xff0c;最近需…

Matplotlib(小案例)

1、3D表面形状的绘制 from mpl_toolkits.mplot3d import Axes3D import matplotlib.pyplot as plt import numpy as np import matplotlib as mplfigplt.figure() axfig.add_subplot(111,projection3d)unp.linspace(0,2*np.pi,100) vnp.linspace(0,np.pi,100) x10*np.outer(n…

python+unity手势控制地球大小

效果图如下 具体操作如下 1 在unity窗口添加一个球体 2 给球体添加材质,材质图片使用地球图片 地球图片如下 unity材质设置截图如下 3 编写地球控制脚本 using System.Collections; using System.Collections.Generic; using UnityEngine;public class test : MonoBehavio…

codeforces round 953 div2

A Alice and books 题目&#xff1a; 思路&#xff1a;编号最大的肯定会被读到&#xff0c;所以在编号最大的这一组书中不能存在除去最大编号的外书外页数最大的书&#xff0c;并且在另一堆中这本书的编号也应该是最大值 代码&#xff1a; #include <iostream>using…

从“小IPD”到“大IPD”

IPD体系实施或变革是一个长期的系统工程&#xff0c;业界经常有从“小IPD”到“大IPD”的说法。 像华为当年就是从“产品开发流程”开始推行&#xff0c;算是很小范围的“小IPD”了&#xff0c;后面逐步扩大为“大IPD”&#xff0c;如上图所示。其中前端的市场管理&#xff08;…

【讲解下Pip换源】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

高危CNVD|用友-U8-OA基础版存在文件上传漏洞getshell

漏洞描述 用友U8-OA基础版存在任意文件覆盖写入漏洞 漏洞说明&#xff1a;用友U8-OA基础版因为代码问题&#xff0c;存在任意文件覆盖写入漏洞&#xff0c;可以覆盖写入系统中存在的文件&#xff0c;可getshell。FOFA指纹: body"致远" && "/yyoa/&quo…

产品经理必须要知道的电商项目搭建中需要用到的常见的电商API接口

今天主要分享产品经理设计电商类应用需要了解的常用API&#xff1f; 为什么产品经理需要了解常用电商API接口呢&#xff1f; 1.开需求会&#xff0c;提了新的需求&#xff0c;开发说&#xff0c;你这个需求太复杂&#xff0c;光接口就有20几个&#xff0c;按照排期根本做不完…