前言

写这么多年 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. 人类高质量 Java 学习路线【一条龙版】

    Java 学习路线一条龙版 by 鱼皮. 原创不易,请勿抄袭,违者必究! 大家好,我是鱼皮.现在网上的编程资料实在太多了,而且人人肯定都说自己的最好,那就导致大家又不知道怎么选了.大部分的博主推荐资源 ...

  2. web259(超详细)

    SoapClient看完之后还不是很懂的话就可以去下面的两个网址去补充一下 SoapClient 是 PHP 中用于与 SOAP(Simple Object Access Protocol)服务进行通 ...

  3. oeasy教您玩转vim - 84 - # 命令command

    ​ 命令 command 回忆 关于 函数function 可以调用别的函数 :call append(0,"oeasy o2z o3z") 还可以执行表达式 :call exec ...

  4. Web 开发技术栈

    Web 开发技术栈 Web 是什么? 简单地说,网络是一个遍布全球的网络,它连接大量设备并允许它们相互通信 Internet 上的网站托管在称为服务器的设备上,当与 Internet 上的网页交互时, ...

  5. mybatisplus实现一次多表联查+分页查询

    众所周知,mybatisplus非常好用,但是他不好用就不好用在不可以多表联查.在mybatisplusjoin中提供了联查的方法,那个参数我没看懂Orz 不过,历经千辛万苦,我通过xml终于写出来了 ...

  6. 《最新出炉》系列入门篇-Python+Playwright自动化测试-56- 多文件上传 - 下篇

    1.简介 前边的两篇文章中,宏哥分别对input控件上传文件和非input控件上传文件进行了从理论到实践地讲解和介绍,但是后来又有人提出疑问,前边讲解和介绍的都是上传一个文件,如果上传多个文件,Pla ...

  7. mysql中的隐式转换导致全表扫描

    mysql中的隐式转换导致全表扫描 在mysql查询中,当查询条件左右两侧类型不匹配的时候会发生隐式转换,可能导致查询无法使用索引.下面分析两种隐式转换的情况 看表结构 phone为 int类型,na ...

  8. Windows安装虚拟机软件-VirtualBox

    1.VirtualBox简介 VirtualBox号称是最强的开源免费虚拟机软件,它不仅具有丰富的特色,而且性能也很优异. 它简单易用,可虚拟的系统包括Windows.Mac OS X.Linux.O ...

  9. 【Java】Collection子接口:其一 List 列接口

    Collection子接口 其一:List接口List 接口存储结构:元素有序,且可重复,每个元素都有对应的索引根据索引获取容器元素 实现类有:ArrayList.LinkedList.Vector ...

  10. 电视家APP会员退款了 —— 网络电视APP的合规性处理

    今天打开家里网络电视的时候突然发现"电视家APP"居然无法使用了,随后发现账户关联的银行卡收到退款了,于是好奇的研究了一下这个事情. 由于家里是在农村,闭路电视的图像效果很差,估计 ...