重学c#系列——订阅发布与事件[二十六]
前言
简单介绍一下订阅发布与事件。
正文
先来看一下委托的订阅与发布。
public delegate void TestDelegate();
public class Cat
{
public TestDelegate testDelegate;
public void call()
{
testDelegate?.Invoke();
}
}
public class BlackMouse
{
public void listen()
{
}
}
public class WhiteMouse
{
public void listen()
{
}
}
代码还是经典的猫和老鼠。
然后运行:
static void Main(string[] args)
{
WhiteMouse whiteMouse = new WhiteMouse();
BlackMouse blackMouse = new BlackMouse();
Cat cat = new Cat();
cat.testDelegate += whiteMouse.listen;
cat.testDelegate += blackMouse.listen;
cat.call();
}
当猫调用call的时候,那么白老鼠和黑老鼠就会听到。
这种就是发布订阅模式了,通过委托多播实现的。
值得注意的是委托多播是按照顺序执行的,比如whiteMouse.listen 中抛出异常,那么blackMouse.listen是不会进行执行的。
这是委托实现发布订阅的一个特点。那么有没有什么办法解决呢?有的。
在c# 中,通过委托实现事件。
public class Cat
{
public TestDelegate testDelegate;
public void call()
{
var methods = testDelegate?.GetInvocationList();
if (methods == null)
{
return;
}
foreach (var m in methods)
{
((TestDelegate)m)();
}
}
}
手动执行委托列表,这样可以根据自己的业务来执行。
static void Main(string[] args)
{
WhiteMouse whiteMouse = new WhiteMouse();
BlackMouse blackMouse = new BlackMouse();
Cat cat = new Cat();
cat.TestEvent += whiteMouse.listen;
cat.TestEvent += whiteMouse.listen;
cat.call();
}
那么什么是事件呢? 事件是当做出一系列操作的时候能做根据这些操作做出另外一些列操作,类似发布订阅模式,这是事件的概念。
那么c# 怎么来使用事件呢?
就是委托封装了一层。
那么这样做有什么用处呢?
这样调用和以前没什么区别啊。
区别在于:
事件只能用于+=和-=,不能用于=号。
这样更加符合事件模型,不让其他地方直接进行修改操控。
同样不能直接调动,只能cat 内部调用。
然后事件使用规范是下面这种。
就是有一个EventHandler这样的委托。
里面指明了要传递事件是谁触发的,然后参数要继承EventArgs,EventArgs 没什么特别的,就是有一个概念里面有一个空的选项。
表示没有传递任何参数,之所以有EventArgs 是为了抽象,统一模型。
订阅的也要这样写。
第一个object 是来源,第二个是参数。
如果想自定义参数的话,就是下面这样写:
这样满足我们的大部分需求了,如果有些需要特殊需求的,可以根据自己来定制,看自己的设计了。
然后来看下event 原理。
里面就是对委托的封装。
看第一个框,是把委托定位私有,那么外部就无法直接访问了。
然后生成了两个公共方法add_TestEvent 和 remove_TestEvent来添加订阅。
最后一个框,发现il语句中有event这个字眼,说明程序运行时候真的识别了event,所以event不仅仅是语法糖。
当il中调用的时候的确是调用了add_TestEvent。
可以理解为事件是对委托的封装,实现了一些操作触发了另外一些操作。
因为事件是对委托的封装,那么其实官方也允许我们自己来定义事件。
public class Cat
{
private EventHandler<TestArgs> _eventHandler;
public event EventHandler<TestArgs> TestEvent
{
add {
_eventHandler = (EventHandler<TestArgs>)Delegate.Combine(value, _eventHandler);
}
remove
{
_eventHandler = (EventHandler<TestArgs>)Delegate.Remove(_eventHandler,value);
}
}
public void call()
{
TestArgs testArgs = new TestArgs("Tom");
_eventHandler?.Invoke(this, testArgs);
}
}
事件虽然上面用发布订阅来描述,其实是不准的,发布订阅只是事件的一种模型。
同样因为事件是基于委托实现的,所以依然有那个问题,如果有一个执行有问题,剩下的将不会执行。
执行结果:
如果不符合这种设计模型,还是自己根据业务需求去编写自己的执行代码,上文展示了委托的,事件的是一样的。
然后关于委托是不是链式执行的,有或者更具+=的顺序来执行的,官方并没有说明。
可以简单做下实验:
static void Main(string[] args)
{
TestDelegate a = null;
List<int> test = new List<int>();
var arr = Enumerable.Range(1, 10000).ToArray();
foreach (var i in arr)
{
var c = i;
a += () => test.Add(c);
}
a();
var newArr = test.ToArray();
var flag = true;
for (var i=0; i< arr.Length;i++)
{
if (i+1 != newArr[i])
{
Console.WriteLine("执行顺序不一致");
flag = false;
}
}
Console.WriteLine($"{flag}");
Console.ReadLine();
}
运行多次后,依然是true哈,听说不同机型和net版本不一样运行就不一样,这个先不做判断。
我们来试一下异步。
public delegate Task TestDelegate();
static void Main(string[] args)
{
TestDelegate a = null;
List<int> test = new List<int>();
var arr = Enumerable.Range(1, 10000).ToArray();
foreach (var i in arr)
{
var c = i;
a += async () => {
await Task.Delay(100);
test.Add(c);
};
}
a();
var newArr = test.ToArray();
var flag = true;
for (var i=0; i< arr.Length;i++)
{
if (i+1 != newArr[i])
{
Console.WriteLine("执行顺序不一致");
flag = false;
}
}
Console.WriteLine($"{flag}");
Console.ReadLine();
}
如果是异步的话,那么内部是不会进行等待的,这个是确认的。
另外一个有趣的例子:
public delegate void TestDelegate();
static void Main(string[] args)
{
TestDelegate a = new TestDelegate(() =>
{
Console.WriteLine(1);
});
TestDelegate b = new TestDelegate(() =>
{
Console.WriteLine(2);
});
TestDelegate c = new TestDelegate(() =>
{
Console.WriteLine(3);
});
a += b;
a += c;
a -= b;
a += b;
a();
Console.ReadLine();
}
执行的时候是否空出b,然后再填充b呢? 答案是不是。
上面例子只是再我自己电脑上做的例子,只能说明如果异步是不会形成串联的。
关于多播委托运行的顺序,其实我觉得没有那么重要,如果想设计这种串行的话,最好直接用职责链模式。
因为多播委托,概念主要是多播,没必要关注顺序,如果关注顺序,那么另一种链式模型其实更符合,这是实现业务值得思考的地方。
结
下一节可能是泛型也可能是linq,不确定,会尽快更新完这100多篇。
重学c#系列——订阅发布与事件[二十六]的更多相关文章
- 从零開始学android<ImageSwitcher图片切换组件.二十六.>
ImageSwitcher组件的主要功能是完毕图片的切换显示,比如用户在进行图片浏览的时候.能够通过button点击一张张的切换显示的图片,并且使用ImageSwitcher组件在每次切换的时候也能够 ...
- 重学c#系列——字典(十一)
前言 重学c#系列继续更新,简单看一下字典的源码. 看源码主要是解释一下江湖中的两个传言: 字典foreach 顺序是字典添加的顺序 字典删除元素后,字典顺序将会改变 正文 那么就从实例化开始看起,这 ...
- Web 前端开发人员和设计师必读精华文章【系列二十六】
<Web 前端开发精华文章推荐>2014年第5期(总第26期)和大家见面了.梦想天空博客关注 前端开发 技术,分享各类能够提升网站用户体验的优秀 jQuery 插件,展示前沿的 HTML5 ...
- 重学c#系列——对c#粗浅的认识(一)
前言 什么是c#呢? 首先你是如何读c#的呢?c sharp?或者c 井? 官方读法是:see sharp. 有没有发现开发多年,然后感觉名字不对. tip:为个人重新整理,如学习还是看官网,c# 文 ...
- 重新整理 .net core 实践篇—————领域事件[二十九]
前文 前面整理了仓储层,工作单元模式,同时简单介绍了一下mediator. 那么就mediator在看下领域事件启到了什么作用吧. 正文 这里先注册一下MediatR服务: // 注册中间者:Medi ...
- 重学c#系列——异常续[异常注意事项](七)
前言 对上节异常的补充,也可以说是异常使用的注意事项. 正文 减少try catch的使用 前面提及到,如果一个方法没有实现该方法的效果,那么就应该抛出异常. 如果有约定那么可以按照约定,如果约定有歧 ...
- 重学Golang系列(一): 深入理解 interface和reflect
前言 interface(即接口),是Go语言中一个重要的概念和知识点,而功能强大的reflect正是基于interface.本文即是对Go语言中的interface和reflect基础概念和用法的一 ...
- 重学c#系列——c# 托管和非托管资源(三)
前言 c# 托管和非托管比较重要,因为这涉及到资源的释放. 现在只要在计算机上运行的,无论玩出什么花来,整个什么概念,逃不过输入数据修改数据输出数据(计算机本质),这里面有个数据的输入,那么我们的内存 ...
- 重学c#系列——c# 托管和非托管资源与代码相关(四)
前言 这是续第三节. 概况垃圾回收与我们写代码的关系: 强引用和弱引用 针对共享 Web 承载优化 垃圾回收和性能 应用程序域资源监视 正文 强引用和弱引用 垃圾回收器不能回收仍在引用的对象的内存-- ...
- 重学c#系列——list(十二)
前言 简单介绍一下list. 正文 这里以list为介绍. private static readonly T[] s_emptyArray = new T[0]; public List() { t ...
随机推荐
- 自定义View5 -塔防小游戏:第二篇防御塔随意放置
第一篇:一个防御塔+多个野怪(简易版) 第二篇:防御塔随意放置 自定义View,处理事件分发,up,move,down. 第三篇:防御塔随意放置+多组野怪 第四篇:多波野怪 第五篇:杀死野怪获得金币 ...
- K8s 上的分布式存储集群搭建(Rook/ceph)
转载自:https://mp.weixin.qq.com/s/CdLioTzU4oWI688lqYKXUQ 1 环境准备 1.1 基础环境 3台配置一致的虚拟机 虚拟机配置:4c 8g 虚拟机操作系统 ...
- git commit、git push、git pull、 git fetch、git merge 的含义与区别
git commit:是将本地修改过的文件提交到本地库中: git push:是将本地库中的最新信息发送给远程库: git pull:是从远程获取最新版本到本地,并自动merge: git fetch ...
- 【可视化大屏教程】用Python开发智慧城市数据分析大屏!
目录 一.开发背景 二.讲解代码 2.1 大标题+背景图 2.2 各区县交通事故统计图-系列柱形图 2.3 图书馆建设率-水球图 2.4 当年城市空气质量aqi指数-面积图 2.5 近7年人均生产总值 ...
- 《3-D Deep Learning Approach for Remote Sensing Image Classification》论文笔记
论文题目<3-D Deep Learning Approach for Remote Sensing Image Classification> 论文作者:Amina Ben Hamida ...
- Git 便捷操作
虽然现在有很多图形化的 Git 工具,但是命令行依然 yyds.本文记录了工作中很有用的一些 Git 操作. 1.Fork出来的Git仓库同步代码 背景:有的时候从原仓库fork出了一个新仓库,这个新 ...
- PAT (Basic Level) Practice 1008 数组元素循环右移问题 分数 20
一个数组A中存有N(>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(≥0)个位置,即将A中的数据由(A0A1⋯AN−1)变换为(AN−M⋯AN−1A0A1⋯AN ...
- ERP 系统的核心是什么?有什么作用?
ERP系统的核心就是系统的内部业务逻辑,这也是ERP复杂.专业性的体现!ERP系统需要适配企业的管理思想和业务流程,在技术上面也也要做到快速部署和个性化定制(客户化定制),而这些企业的规模不同.行业不 ...
- OpenDataV低代码平台增加自定义属性编辑
上一篇我们讲到了怎么在OpenDataV中添加自己的组件,为了让大家更快的上手我们的平台,这一次针对自定义属性编辑,我们再来加一篇说明.我们先来看一下OpenDataV中的属性编辑功能. 当我们拖动一 ...
- python-D1-typora软件和计算机入门1
一 typora软件 typora是一款目前非常火爆文本编辑器 1.1 安装 尽量安装在非系统盘符及设置为短路径,方便后面查找 1.2 文件路径 在计算机上就是一个资源的定位坐标,表现为具体在哪里,例 ...