事件是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. 【功耗测试环境预置自动化脚本开发】【切换wifi模式为siso模式】【用到方法*args】

    import os,reimport logginglogging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(filename)s[ ...

  2. 【爆料】-《伯明翰大学学院毕业证书》UCB一模一样原件

    ☞伯明翰大学学院毕业证书[微/Q:865121257◆WeChat:CC6669834]UC毕业证书/联系人Alice[查看点击百度快照查看][留信网学历认证&博士&硕士&海归 ...

  3. python脚本对 mysql数据库进行增删改查操作

    # -*- coding: utf-8 -*-import pymysqlimport xlrd# import codecsconn = pymysql.connect(host='127.0.0. ...

  4. C++ set用法以及迭代器用法

    有关set的一些常用函数 1.begin() / end() 返回首/尾元素迭代器 2.rbegin() / rend() 返回尾/首元素反向迭代器,反向迭代器可以反向遍历容器的迭代器,从下面的程序已 ...

  5. 基于SpringBoot从零构建博客网站 - 技术选型和整合开发环境

    技术选型和整合开发环境 1.技术选型 博客网站是基于SpringBoot整合其它模块而开发的,那么每个模块选择的技术如下: SpringBoot版本选择目前较新的2.1.1.RELEASE版本 持久化 ...

  6. 基于Raft深度优化,腾讯云金融级消息队列CMQ高可靠算法详解

    背景介绍 分布式系统是指一组独立的计算机,通过网络协同工作的系统,客户端看来就如同单台机器在工作.随着互联网时代数据规模的爆发式增长,传统的单机系统在性能和可用性上已经无法胜任,分布式系统具有扩展性强 ...

  7. @GeneratedValue源码解析

    JPA要求每一个实体必须有且只有一个主键,而@GeneratedValue提供了主键的生成策略,这就是@GeneratedValue注解存在的意义.本文将浅析@GeneratedValue的源码. @ ...

  8. myeclipse附加源码进行查看的方法

    在编程过程中,有可能需要用到看源码的情况,那么怎么进行添加源码呢,这里做下记录 首先,先下载javaEE源码(可在网上自由下载) 1.在HttpServlet上右键-->Open Declara ...

  9. 我学Java我傲娇

    今天,终于把学习了一年多的<Java核心技术36讲>第一遍完结了,历时一年多,花出去的钱终于感觉有了第一笔回报. 这是我在极客时间的第一门付费课程,刚出来的时候,正巧自己转到全职Java开 ...

  10. easyui表格自动换行

    表格内容自动换行可以通过设计表格属性  nowrap:false来实现,默认值为true: 但是easyui并未提供,表头自动换行的解决方案,因为一般我们的数据表格列名都是固定的,想换行的话可以通过& ...