前言

写这么多年 C#, 我从来没有写过 EventHandler. 我想应该是因为我没有用 C# 开发过前端的关系, 绝对不是我技术不行哦.

这篇就补上一个学习笔记呗.

参考

C#知识点讲解之C#delegate、event、Action、EventHandler的使用和区别

如何:引发事件和使用事件

介绍

EventHandler 就是观察者模式的实现, 你可以把它完全当作前端 DOM addEventListener 去理解. (当然前端的还有冒泡概念, 会更加复杂一点)

delegate

在理解 EventHandler 之前, 要先了解什么是 delegate (委托).

C# 不像 JS 那样, 函数一等公民. 天生就可以当变量, 参数使用.

在 C# 函数通常是作为类的方法来使用的, 传递的变量, 参数则是对象, 而不是函数(方法).

而 delegate 则赋予了 C# 直接传递函数的能力. 函数变一等公民了.

定义函数类型

public delegate void MyMethod(string name, int age);

这个定义不需要在 class 内哦

参数是一个函数

public class MyClass
{
// 参数是一个函数
public void Run(MyMethod myMethod)
{
myMethod("name", 11);
}
}

参数是一个变量

public static void Main()
{
var myClass = new MyClass();
// 变量是一个函数
MyMethod myMethod = (name, age) =>
{
Console.Write($"{name}, {age}");
};
myClass.Run(myMethod);
}

上面这些代码完全可以用 TypeScript 来实现.

// 定义函数类型
interface MyMethod {
(name: string, age: number) : void
} class MyClass {
// 参数是一个函数
run(myMethod: MyMethod): void {
myMethod('name', 11);
}
}
const myClass = new MyClass();
// 变量是一个函数
const myMethod: MyMethod = (name, age) => {
console.log(`${name}, {age}`);
}
myClass.run(myMethod);

delegate as event

delegate 有一个很奇葩的能力, 那就是它可以用来实现观察者模式. 这点有点像 es5 的 function 被用来实现 class 一样 (没错, 就是来乱的)

我们换一下 class name

public delegate void MyEventHandler(string name, int age);
public class MyElement
{
public MyEventHandler? MyEventHandlers { get; set; }
}

想象它是 DOM, 我们想 addEventListener

var myElement = new MyElement();
MyEventHandler myEventHandler1 = (name, age) => { };
MyEventHandler myEventHandler2 = (name, age) => { };
MyEventHandler myEventHandler3 = (name, age) => { };
myElement.MyEventHandlers += myEventHandler1;
myElement.MyEventHandlers += myEventHandler2;
myElement.MyEventHandlers += myEventHandler3;
myElement.MyEventHandlers -= myEventHandler3;
myElement.MyEventHandlers?.Invoke("name", 11);

+= 就是 addEventListener

-= 就是 removeEventListener

= 就是覆盖, 相等于 remove all + add new one.

如果函数有返回值, 那么最后一个返回值会是 Invoke 的 return value. 当然这个是不正确的, 观察者发布不应该会收到反馈的.

event

delegate as event 太别扭了. 于是 C# 有了一个新概念叫 event, 它就类似 es6 class 的出现那样, event 专注在观察者模式的实现.

public delegate void MyEventHandler(string name, int age);
public class MyElement
{
public event MyEventHandler? MyEventHandlers;
}

delegate 函数类型定义依然是需要的, 但是 MyElenent 的函数属性多了 event keyword 在前面. 这样就声明了这个属性是用来搞观察者模式的, 而不仅仅是一个函数属性.

var myElement = new MyElement();
MyEventHandler myEventHandler1 = (name, age) => { };
MyEventHandler myEventHandler2 = (name, age) => { };
MyEventHandler myEventHandler3 = (name, age) => { };
myElement.MyEventHandlers += myEventHandler1;
myElement.MyEventHandlers += myEventHandler2;
myElement.MyEventHandlers += myEventHandler3;
myElement.MyEventHandlers -= myEventHandler3;
myElement.MyEventHandlers = myEventHandler3; // 报错! event 只可以 += 和 -= 不可以 =
myElement.MyEventHandlers?.Invoke("name", 11); // 报错! dispatch 只可以让 MyElement 内部实现.

最后两行会报错. "=" 的效果是 override 全部 handlers 这个操作有点过分了. 所以 event 出现后也平昌了这过大的能力.

另一点是, 外部不再直接 Invoke handlers, 取而代之的是应该要通过一个 Dispatch 操作去完成. 这也比较符合大家对观察者模式的实现和使用直觉.

public class MyElement
{
public event MyEventHandler? MyEventHandlers;
public void Dispatch()
{
MyEventHandlers?.Invoke("name", 11);
}
}

EventHandler

event 已经算及格了. 但是 C# 任然搞了一个更上层的语法 EventHandler. 它又更贴近观察者模式的实现了.

public class MyEventArgs : EventArgs
{
public string Name { get; set; } = "";
public int Age { get; set; }
}

首先 delegate 不需要了. 我们只定义传递的 parameters (EventArgs) 就好. 彻底和 delegate 分家 (不可能再混淆了)

public class MyElement
{
public event EventHandler<MyEventArgs>? MyEventHandlers;
public void Dispatch()
{
MyEventHandlers?.Invoke(this, new MyEventArgs { Name = "name", Age = 11 });
}
}

没有了 delegate 就需要有个替代, 那就是 EventHandler 类. 这是是 C# build-in 的.

Invoke 的时候, 需要把 obj 和 args 传入.

使用如下

var myElement = new MyElement();
EventHandler<MyEventArgs> myEventHandler1 = (obj, args) => { };
EventHandler<MyEventArgs> myEventHandler2 = (obj, args) => { };
EventHandler<MyEventArgs> myEventHandler3 = (obj, args) => { };
myElement.MyEventHandlers += myEventHandler1;
myElement.MyEventHandlers += myEventHandler2;
myElement.MyEventHandlers += myEventHandler3;
myElement.MyEventHandlers -= myEventHandler3;
myElement.Dispatch();

这一套下来, 和 DOM – Event Listener 真的很像了. 原来 C# 这么多年就是用这套来搞前端的丫.

Cancelation

观察者模式再发布 event 后, 大家唯一的沟通方式就是传递的 event 对象.

比如 DOM 的 event.preventDefault 用来阻止游览器默认事件处理就是利用了这一点.

那 C# 当然也可以实现这些. 毕竟 event 对象是我们自己定义的嘛.

上面我继承了 C# build-in 的 EventArgs, 它还有一个 CancelEventArgs.

里面有一个 Cancel 属性, 你可以把它当成 preventDefault 看待.

来一个完整版的:

public class MyEventArgs : CancelEventArgs
{
public string Name { get; set; } = "";
public int Age { get; set; }
} public class MyElement
{
public event EventHandler<MyEventArgs>? MyEventHandlers;
public void Dispatch(MyEventArgs args)
{
MyEventHandlers?.Invoke(this, args);
}
}
public static class Program
{
public static void Main()
{
var myElement = new MyElement();
EventHandler<MyEventArgs> myEventHandler1 = (obj, args) => { args.Cancel = true; };
EventHandler<MyEventArgs> myEventHandler2 = (obj, args) => { if (args.Cancel) return; };
myElement.MyEventHandlers += myEventHandler1;
myElement.MyEventHandlers += myEventHandler2;
var args = new MyEventArgs { Name = "Derrick", Age = 11 }
myElement.Dispatch(args);
if (args.Cancel)
{
// skip
}
}
}

结语

本篇简单介绍了 C# 实现的观察者模式. 通常这些都是用来处理前端事件. 当然也可以用在任何你想使用观察者模式的地方.

今天, 我是意外的在 HtmlSanitizer 源码中看见了它使用 EventHandler, 好奇下才认真的研究了一下. 顺便分享一下

如果我们想 whitelist 一下 attributes 不要被 sanitize, 除了直接提供一个 list 之外, 它还提供了一个方式.

那就是 addEventListener + preventDefault. 它在 reomove attribute 前会先 dispatch. 如果发现 cancel 那它就不 remove attribute 了.

这就类似与 click <a> element, 游览器的行为是跳转, 但是如果我们 addEventListener + preventDefault 那它就不跳了.

同样的思路.

C# – delegate, event, EventHandler的更多相关文章

  1. Delegate & Event

    Long time without coding,貌似对programming都失去了曾有的一点点sense了,今日有空再细瞄一下.net的委托和事件. Delegate 首先,委托用于引用一类具有相 ...

  2. event & EventHandler

    [event & EventHandler] 在老C#中EventHandler指的是一个需要定义一个delegate,这个delegate是回调的规范.例如: public delegate ...

  3. C# delegate event func action 匿名方法 lambda表达式

    delegate event action func 匿名方法 lambda表达式 delegate类似c++的函数指针,但是是类型安全的,可以指向多个函数, public delegate void ...

  4. [UE4] C++实现Delegate Event实例(例子、example、sample)

    转自:http://aigo.iteye.com/blog/2301010 虽然官方doc上说Event的Binding方式跟Multi-Cast用法完全一样,Multi-Cast论坛上也有很多例子, ...

  5. [C#] Delegate, Multicase delegate, Event

    声明:这篇博客翻译自:https://www.codeproject.com/Articles/1061085/Delegates-Multicast-delegates-and-Events-in- ...

  6. C# delegate & event

    public delegate void MyDelegate(string mydelegate);//声明一个delegate对象 //实现有相同参数和返回值的函数        public v ...

  7. Delegate&Event

    Delegate 1.基本类: public class Student { public int Id { get; set; } public string Name { get; set; } ...

  8. Delegate event 委托事件---两个From窗体使用委托事件

    窗体如下:   public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void b ...

  9. 委托与事件--delegate&&event

    委托 访问修饰符 delegate 返回值 委托名(参数); public delegate void NoReturnNoPara(); public void NoReturnNoParaMeth ...

  10. delegate, event - 里面涉及的参数类型必须完全一致,子类是不行的

    public void TestF() { Test += Fun; } public void Fun(Person p) { }  // 如 Person变成 SubPerson,则报错..pub ...

随机推荐

  1. Error: Dynamic require of "path" is not supported

    failed to load config from D:\BaiduSyncdisk\vue3\sys-manager\vite.config.jserror when starting dev s ...

  2. Linux 提权-NFS 共享

    本文通过 Google 翻译 NFS Share no_root_squash – Linux Privilege Escalation 这篇文章所产生,本人仅是对机器翻译中部分表达别扭的字词进行了校 ...

  3. python解决urllib发送请求报错:urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:xxxx)>

    在使用urllib.request.Request(url)前,添加代码放到最前面 import ssl ssl._create_default_https_context = ssl._create ...

  4. ceph 003 对osd操作 对存储池操作 存储池配额 存储池快照 pgp

    主机被加入集群时,会自动被分配角色以达到集群的默认状态.(mon,mgr之类) 想要超过默认状态可以进行设置 ceph容器与客户端 ceph集群的客户端 需要 ceph-common 软件包 ceph ...

  5. layui下拉框的数据如何直接从数据库提取(动态赋值)

    代码说明部分 第一步:先把layui官方给的模板粘到自己的前端注:下面的代码是我直接从layui官网粘过来的 <div class="layui-form-item"> ...

  6. 对比python学julia(第三章:游戏编程)--(第三节)疯狂摩托(3)

    3.3.    编程实现 2.  控制摩托车和箱子 在这个步骤中,将编程控制摩托车和箱子角色的运动,让摩托车在沙漠公路上能够加速或减速行驶,在碰到箱子时能够停止,以及显示麾托车的行驶速度和里程等. ( ...

  7. 【Java】Collection 集合框架概述

    Collection 集合框架概述 1.集合.数组都是为了存储数据而产生的 2.为什么需要集合?为了更灵活方便的存储数据,且集合能存储的容量比数组更大 3.存储的概念还停留在内存活动范围内,也只是短暂 ...

  8. 【SpringMVC】08 Post请求乱码

    这是以Post请求方式接受来的中文字符乱码 就像之前JavaWeb阶段的情况一样 所以,按照JavaWeb的方法,我们应该只需要在获取之前设置好编码处理就行了 可以先从这个处理方法开始试试,但是参数在 ...

  9. 【WEB】URL文件

    早些年接触电脑的时候就有这个东西,去网站上下载盗版游戏,网站会附加这种URL文件 双击运行之后是打开浏览器跳转到该文件描述的网址 我从来没想过这东西里面写的是什么 百度经验: https://baij ...

  10. 【Java】API 时区ID类

    主要是用以获取时区ID @Test void contextLoads() { ZoneId zoneId = ZoneId.systemDefault(); // System.out.printl ...