事件是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#的认知。

----------------------------------------------------------------------------------------------------

注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!
若您觉得这篇文章还不错,请点击下方的【推荐】,非常感谢!

C#语法——事件,逐渐边缘化的大哥。的更多相关文章

  1. C#语法——反射,架构师的入门基础。

    前言 编程其实就是写代码,而写代码目的就是实现业务,所以,语法和框架也是为了实现业务而存在的.因此,不管多么高大上的目标,实质上都是业务. 所以,我认为不要把写代码上升到科学的高度.上升到艺术就可以了 ...

  2. C#语法——消息,MVVM的核心技术。

    在C#中消息有两个指向,一个指向Message,一个指向INotify.这里主要讲INotify. INotify也有人称之为[通知],不管叫消息还是通知,都是一个意思,就是传递信息. 消息的定义 I ...

  3. jQuery_第五章_事件和动画

    Jquery中的事件与动画 一.window.onload和$(document).read()的细微差别 (1)执行时机 window.onload:所有元素(包括元素的所有关联文件)完全加载到浏览 ...

  4. XAML实例教程系列 - 事件(Event) 五

    Kevin Fan分享开发经验,记录开发点滴 XAML实例教程系列 - 事件(Event) 2012-06-19 01:36 by jv9, 1727 阅读, 7 评论, 收藏, 编辑 Events, ...

  5. vue的事件绑定

    vue事件有两方面内容:DOM事件 和 自定义事件. DOM事件 vue中采用DOM2级事件的处理方式,为IE9以上的浏览器服务.下面我们先来讲解一下什么是DOM2级事件吧! JS中DOM0级事件有两 ...

  6. PB数据管道

    数据管道提供了一种不同数据库之间传递数据和(或)表结构的方法. 数据管道对象 要完毕数据管道的功能须要提供例如以下内容: 须要数据源和目标数据库,并可以和这两个数据库正常联接 须要源数据库中的哪些表: ...

  7. vue2.0父子组件之间通信

    父组件是通过props属性给子组件通信的来看下代码: 父组件: <parent> <child :child-com="content"></chil ...

  8. vue2.0组件通信小总结

    1.父组件->子组件 父组件 <parent> <child :child-msg="msg"></child>//这里必须要用 - 代替 ...

  9. Vue针对性笔记

    Github原文阅读 MVVM(Model-View-ViewModel)模型 MVVM分为Model.View.ViewModel三部分. Model代表数据模型,定义数据和业务逻辑,访问数据层 V ...

随机推荐

  1. Shell脚本的三种执行方式

    Shell脚本的执行方式可以有以下几种: 方式一:  ./script.sh # 利用小数点来执行 方式二:  sh script.sh 或 bash script.sh # 利用bash(sh)来执 ...

  2. RabbitMQ+Spring 结合使用

    1:创建一个Maven工程,pom.xml文件如下: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi ...

  3. 说说new 和 malloc()

    熟悉c++的朋友应该都知道,c++提供给了程序员与硬件打交道的可能性,比如说内存管理.一个高水平的c++程序员可以将c++程序的性能优化到极致,榨干硬件资源.而现在我想说说与内存管理有关的new 和 ...

  4. FreeSql 新的八大骚功能,.NETCore 你必须晓得的 ORM

    前言 FreeSql 目前版本号 0.5.5,预计明年元旦发布 1.0.0,切莫小看了版本号,目前单元测试方法1350+,并且每个方法内的涵盖面又比较广(不信的话见下图),每一次版本发布都作了较多的测 ...

  5. .NET 反编译调试神器:dnSpy了解一下

    如果客户环境出了问题,而又无法快速定位问题,可以借助dnSpy进行反编译调试跟踪. 可前往dnSpy官网下载或直接从我的分享链接下载(内置包含.NET Framework 4.7.1,若运行提示需要安 ...

  6. #Java学习之路——基础阶段二(第九篇)

    我的学习阶段是跟着CZBK黑马的双源课程,学习目标以及博客是为了审查自己的学习情况,毕竟看一遍,敲一遍,和自己归纳总结一遍有着很大的区别,在此期间我会参杂Java疯狂讲义(第四版)里面的内容. 前言: ...

  7. Docker入门学习

    Python爬虫 最近断断续续的写了几篇Python的学习心得,由于有开发经验的同学来说上手还是比较容易,而且Python提供了强大的第三方库,做一个小的示例程序还是比较简单,这不我之前就是针对Pyt ...

  8. JAVA匿名内部类(Anonymous Classes)

    1.前言 匿名内部类在我们JAVA程序员的日常工作中经常要用到,但是很多时候也只是照本宣科地用,虽然也在用,但往往忽略了以下几点:为什么能这么用?匿名内部类的语法是怎样的?有哪些限制?因此,最近,我在 ...

  9. WebGL three.js学习笔记 纹理贴图模拟太阳系运转

    纹理贴图的应用以及实现一个太阳系的自转公转 点击查看demo演示 demo地址:https://nsytsqdtn.github.io/demo/solar/solar three.js中的纹理 纹理 ...

  10. 第6章 令牌撤销端点(Token Revocation Endpoint) - IdentityModel 中文文档(v1.0.0)

    OAuth 2.0令牌撤销的客户端库是作为扩展方法提供的HttpClient. 以下代码撤消撤销端点处的访问令牌令牌: var client = new HttpClient(); var resul ...