事件是 C# 的基础之一, 学好事件对于了解. NET 框架大有好处.
事件最常见的比喻就是订阅, 即, 如果你订阅了我的博客, 那么, 当我发布新博客的时候, 你就会得到通知.
而这个过程就是事件, 或者说是事件运行的轨迹.
事件是发散, 以我的博客为核心, 向所有订阅者发送消息. 我们把这种发散称之为 [多播].
最常见的事件用途是窗体编程, 在 Windows 窗体应用程序和 WPF 应用程序中.
当在窗体中点击按钮, 移动鼠标等事件时, 相应的后台程序会收到通知, 再执行代码.
事件的定义
官方对事件的说明是这样的: 类或对象可以通过事件向其他类或对象通知发生的相关事情.
换成正常语言就是, 事件可以定义成静态的或普通的, 所以事件就可以由声明的对象调用, 也可以直接通过类调用静态事件.
事件是 C# 中的一种类型, 除了框架为我们定义好的事件外, 我们还可以自定义事件, 用 event 关键字来声明.
下面我们来看最基础的事件定义.
- public delegate void TestDelegate(string message);
- public event TestDelegate testEvent;
我们首先定义了一个委托, 然后利用 event 关键字, 定义一个事件.
整体上看, 好像就是在定义一个委托, 只是在委托的定义之前, 加了个 event 关键字.
没错, 事件的定义就是这样, 因为要声明一个事件, 需要两个元素:
一, 标识提供对事件的响应的方法的委托.
二, 一个类, 用存储事件的数据. 即, 事件要定义在类中.
下面我们来为这个事件赋值.
- public void Init()
- {
- testEvent += new TestDelegate(EventSyntax_testEvent);
- testEvent += EventSyntax_testEvent;
- }
- private void EventSyntax_testEvent(string message)
- {
- Console.WriteLine(message);
- }
如代码所示, 我们使用了 += 这个符号来为事件赋值, 赋值的内容是一个委托和一个函数.
其中 += 我们将他理解为 [添加] .
代码中, 我们使用两种赋值模式, 但实际上都是为事件 testEvent 添加一个委.
第二种将函数直接 [添加] 到事件中, 编译时也会把函数转换成委托 [添加] 到事件中.
系统提供事件
C# 的框架都很经典, 而每个经典框架都为我们提供了一些经典事件.
由于事件必须 [标识响应方法的委托], 所以这些事件所使用的委托都有一个共同的特点, 命名中包含 Event.
比如 EventHandler,CancelEventHandler,RoutedEventHandler,ContextMenuEventHandler 等.
其中最经典的就是 EventHandler 和 RoutedEventHandler.
EventHandler:
EventHandler 定义如下
- [SerializableAttribute]
- [ComVisibleAttribute(true)]
- public delegate void EventHandler(
- object sender,
- EventArgs e
- )
他包含了两个参数, 即当我们为事件添加 EventHandler 委托后, 再去触发该事件; 被触发的委托将得到 object sender 和 EventArgs e 两个参数.
sender: 代表源, 即触发该事件的控件.
e: 代表事件参数, 即触发该事件后, 事件为被触发的委托, 传递了一些参数, 以方便委托在处理数据时, 更便捷.
根据这个原理, 我们可以分析出很多东西.
比如, 当控件 DataGrid 的事件被触发时, 只要查看一下 sender 的真实类型, 就可以知道, 到底是 DataGrid 触发的事件, 还是 DataGridRow 或 DataGridCell 触发的了.
RoutedEventHandler:
RoutedEventHandler 即路由事件, 他的定义如下
- public delegate void RoutedEventHandler(
- Object sender,
- RoutedEventArgs e
- )
RoutedEventHandler 也为我们提供了 sender 和 e 两个参数.
但 RoutedEventHandler 特别之处是, 他的 sender 并不一定是真实的源, 因为他是一个冒泡路由事件, 即上升事件.
这里如果大家有好奇心去看官方文档, 那么会在相关的介绍中看到两个单词 sender 和 source.
通过这两个单词, 我们会清晰的了解路由事件. 简单描述一下 sender 和 source, 它们一个是发送者, 一个是源.
在 EventHandler 中, sender 即 source, 因为它是直接事件. 而在冒泡事件中, sender 不一定等于 source. 即发送者不一定是源.
下面我们用 WPF 来看看路由事件.
我们首先在 XAML 页面定义一个 RadioButton 按钮, 然后设置他的模板是 Button. 然后分别定义各自的 Click 方法.
Xaml 页面如下:
<RadioButton Click="btnParent_Click">
<RadioButton.Template>
<ControlTemplate>
<StackPanel>
<TextBlock Text="我的名字"></TextBlock>
<Button Content="Kiba518" Click="btnClild_Click"></Button>
</StackPanel>
</ControlTemplate>
</RadioButton.Template>
</RadioButton>
cs 文件事件如下:
- private void btnParent_Click(object sender, RoutedEventArgs e)
- {
- string type = sender.GetType().ToString();//RadioButton
- }
- private void btnClild_Click(object sender, RoutedEventArgs e)
- {
- string type = sender.GetType().ToString();//Button
- }
运行起来, 我们点击按钮, 通过断点我们可以看到, 我们点击的按钮触发了 btnClild_Click 和 btnParent_Click 事件.
顺序是先 btnClild_Click 后 btnParent_Click.
通过获取 sender 的类型, 我也可以看到, btnClild_Click 的 sender 类型是 Button, 而 btnParent_Click 的 sernder 类型是 RadioButton.
事件驱动编程
事件驱动编程这个概念给我的感觉很怪, 因为一直用 C#, 而 C# 的很多框架都是事件驱动的, 所以一直觉得事件驱动是理所当然.
而当事件驱动设计这个词经常出现后, 反而感觉怪怪的.
就好像, 天天吃大米饭, 突然有一天, 所有人都说大米饭好香的感觉一样, 你一听就感觉怪怪的.
因为事件驱动对于 C# 开发而言, 实在太普通了. 当然, 这也得益于微软框架做的实在是太好了.
所以, 我也不知道如何在 C# 里讲事件驱动编程. 因为使用 C# 的框架就是使用事件驱动编程.
事件和委托到底是什么关系?
事件是用来多播的, 并且用委托来为事件赋值, 可以说, 事件是基于委托来实现的.
但委托中也有多播, 那为什么要单独弄出来一个事件呢?
首先, 存在即合理, 事件一定有他存在的意义.
事件存在的意义
我对事件存在的意义是这样理解的. 我们在 C# 编写框架时, 几乎不用委托的多播, 因为委托的多播和事件存在严重的二义性. 虽然编写框架的人学会了使用委托的多播, 但使用框架的同事可能并还不太熟练, 而且 C# 框架中, 大多是使用事件来进行多播的.
所以委托的多播和事件一起使用的框架, 会造成使用这个框架的初级开发者很多困惑, 而这种困惑, 会产生很多不必要的问题.
比如, 你定义了一个委托, 另一个开发者用这个委托做了个多播, 当第三个开发者来维护这段代码时, 如果他是新手, 不了解委托的多播, 那就很有可能只修改了委托调用的代码. 而没有去同步多播这个委托的代码. 那系统就产生了隐藏的 bug.
那么, 事件和委托到底是什么关系呢?
事件与委托的确存在千丝万缕的关系, 怎么讲都是正确的. 但, C# 开发者只需要记住, 他们俩没关系即可. 在 C# 事件是事件, 委托是委托. 两者就如同 int 和 string 一样, 没有任何关系.
原因很简单, 学习的过程中尽量降低概念混淆. 而且, 在 C# 开发中, 好的架构者也通常会将事件和委托分离, 所以, 就认为事件和委托没有关系即可.
结语
其实事件很好理解, 一点不复杂. 我在写这篇文章的过程中, 也没想到什么特别的或者说比较高级的用法.
但真实的应用场景中, 我的感觉是, 随着 MVVM 的成长, 事件其实在被逐渐抛弃. 虽然微软做了很多经典的事件驱动框架. 但那都是过去了.
比如 WPF 虽然支持事件驱动, 但 MVVM 在 WPF 下的表现堪称完美, 所以 WPF 下的事件几乎没有人用了.
再比如前端的 Angularjs 等框架, 提供了优质的 MVVM 使用效果, 也让新的前端设计师逐渐放弃了事件.
所以, 事件在未来的编程中, 很可能将不在有那么重要的地位了. 但学好事件, 对于我们理解微软框架, 还是有很大帮助的.
- C# 语法 -- 元组类型
- C# 语法 -- 泛型的多种应用
- C# 语法 --await 与 async 的正确打开方式
- C# 语法 -- 委托, 架构的血液
我对 C# 的认知.
----------------------------------------------------------------------------------------------------
来源: https://www.cnblogs.com/kiba/p/9436431.html