各位客官!鼠标点击一个Button之后究竟发生了什么?您知道么?(C#)
在谈论主题之前,让我们先简单回顾下事件的基础知识吧!
我们知道事件有发出(raises)事件的源,即event sender,也有接收事件通知(notifications)的接收者,即event receiver。因此,若要设计事件必须从发送者和接受者两个Object分别着手设计。其中,发送方类型的设计一般有四个步骤:
- 定义类型来容纳所有需要发送给事件通知接收者的信息。
- 定义事件成员。注意这里事件成员的类型可以是字段或属性,定义成不同的类型,编译生成的代码有较大的不同,需注意。
- 定义负责引发事件的方法来通知事件的登记对象。
- 定义一个方法将输入转化为期望的事件。注意,如果在发起事件时事件发送者需要传递信息,那么需要定义这个方法,将输入传递给封装的EventArgs对象,然后调用步骤3的方法,并将EventArgs对象传递给该方法。如果不需要传递信息则不需定义该方法,直接调用步骤3中的方法即可。
接收方类型设计一般有三个步骤:
- 设计接收方类型的事件处理方法。
- 设计接收方类型的注销对事件关注的方法,和步骤3中做的正好相反。
- 将发送方类型的实例对象以参数形式传到接收方类型的实例构造器中(或者在接收方类型的实例构造器中new一个发送方类型的实例,实际上WinForm应用中就是这么做的,在Form构造器中new一个button的实例对象),然后将接收方类型中的回调函数登记到发送方的事件成员上。
好了,基础知识到此结束,不懂的去看书本哈!下面让我们回到正题上,来看看在WinForm窗体应用中,当我们单击窗体上的一个Button之后究竟发生了什么。
在实际开发窗体应用时,我们一般都是直接将一个Button拖到Form上,然后Visual Studio会为我们新建一个Button实例对象,并设置它的样式。当我们点击按钮之后,会为我们生成如下的代码:
private void button1_Click_1(object sender, EventArgs e)
{ }
那我们就会想了,这部分代码属于接收方Form窗体对象的事件处理方法。那事件的发送方Button的负责引发(raises)事件的方法在哪里呢?在一开始用C#的事件时,我并没有对此深究,导致一直模模糊糊的,现在让我们一起看下Button的引发事件的方法究竟躲在哪里吧!
我们在button1_Click_1事件处理方法这里打一个breakpoint,然后运行程序并单击button1按钮。单步执行并查看程序的调用栈,如下图所示。

图1 单步执行程序并查看调用栈
我们看到,在button1_Click_1事件处理方法调用之前,调用了Control.OnClick方法,我们可以通过反射工具查看它的源代码,如下图所示:

图2 OnClick方法源码
从上图最后一行代码可以看出OnClick就是Button实际执行的引发(raises)事件的方法。也许你又有疑问了,base.Events[EventClick]是什么东东?其实,它是一个委托类型的list结构,可以容纳一个以上的委托,使用线性搜索算法来查找其中的条目,在委托条目较多时,查找效率会下降。EventClick是一个静态只读key值,通常赋以new Object(),用来索引事件成员(EventHandler类型)。好了,到这里,事件是不是就可以说懂了呢?不,我们还遗忘了一个点,那就是在声明事件成员时,声明成字段和属性有区别么?如果有区别,那区别大吗?下面让我们首先看一下声明成字段的情况。

图3 将MyEvent事件成员定义成字段形式。

图4 字段形式事件MyEvent的源代码
从图3和4可以看到,本质上来看,以字段形式定义的事件成员,编译器compile之后自动生成的add_MyEvent和remove_MyEvent两个方法是线程安全的,这意味着客户端程序每次向事件添加或删除一个委托时,必须先获得lock,这在非多线程开发的情形下加大了开销,毕竟获取和释放lock是一个耗费时间的过程。

图5 将MyCustomEvent事件成员定义成属性形式

图6 属性形式事件MyCustomEvent的源代码
从图5和6可以看到,以属性方式声明的事件成员,由于我们事先就定义好了add和remove方法的实现(其实这里就是所谓的“显式实现事件”,这样我们就能控制add和remove方法处理回调委托的方式),在编译之后,仅仅是向一个list中加入和删除委托,并没有考虑多线程的情形,因此,在非多线程的开发中效率较高。注意,事件属性的定义语法和一般的get或set访问器属性不同,事件属性定义中使用add和remove方法。
实际上,我们在Form窗体上拖放一个Button按钮控件时,就是用的属性形式的事件成员定义。如下图7和8所示。

图7 设计器自动生成的绑定Click事件代码

图8 Click事件成员的源代码形式
以上就是关于点击一个button发生的事情的真相了,客官,您弄懂了么?
各位客官!鼠标点击一个Button之后究竟发生了什么?您知道么?(C#)的更多相关文章
- 【代码笔记】iOS-点击一个button,出6个button
一,效果图. 二,工程图. 三,代码. RootViewController.h #import <UIKit/UIKit.h> //加入头文件 #import "DCPathB ...
- 微信小程序中 不点击picker 点击一个button 怎么调用picker 弹出选择框
把按钮放在picker区域里就好了 picker本身就是一个区域 <picker mode = "selector" class='info' bindchange=&quo ...
- 从点击Button到弹出一个MessageBox, 背后发生了什么
思考一个最简单的程序行为:我们的Dialog上有一个Button, 当用户用鼠标点击这个Button时, 我们弹出一个MessageBox. 这个看似简单的行为, 谁能说清楚它是如何运行起来的,背后究 ...
- 从点击Button到弹出一个MessageBox, 背后发生了什么(每个UI线程都有一个ThreadInfo结构, 里面包含4个队列和一些标志位)
思考一个最简单的程序行为:我们的Dialog上有一个Button, 当用户用鼠标点击这个Button时, 我们弹出一个MessageBox. 这个看似简单的行为, 谁能说清楚它是如何运行起来的,背 ...
- [python] 1、python鼠标点击、移动事件应用——写一个自动下载百度音乐的程序
1.问题描述: 最近百度总爱做一些破坏用户信任度的事——文库金币变券.网盘限速,吓得我赶紧想办法把存在百度云音乐中的歌曲下载到本地. http://yinyueyun.baidu.com/ 可问题是云 ...
- jQuery 二级菜单,一次显示一个小类 鼠标点击显示小类
jQuery 二级菜单,一次显示一个小类 鼠标点击显示小类 本例有另外2个关联案例,演示地址分别为2.php,3.php 演示 XML/HTML Code <div class="ar ...
- 每天一个JavaScript实例-铺货鼠标点击位置并将元素移动到该位置
<!DOCTYPE html> <html> <head> <meta http-equiv="Content-Type" content ...
- 问题:asp.net 点击button按钮调到页面顶部;结果:asp.net点击一个按钮,使页面跳转到本面页上的指定位置
asp.net点击一个按钮,使页面跳转到本面页上的指定位置 (2011-04-19 16:46:51) 转载▼ 标签: it 最近在做一个项目. 用到标题所说的功能. 实现方法: 1.在aspx中 ...
- 【Unity3D基础】让物体动起来①--UGUI鼠标点击移动
背景 首先还是先声明自己是比较笨的一个人,总是找不到高效的学习方法,目前自己学习Unity3D的方式主要是两种,一种是直接看高质量的源码,另一种是光看不行还要自己动手,自己写一些有代表性的小程序,这也 ...
随机推荐
- python socket 详细介绍
Python 提供了两个基本的 socket 模块. 第一个是 Socket,它提供了标准的 BSD Sockets API. 第二个是 SocketServer, 它提供了服务器中心类,可以简化网络 ...
- MySQL查询结果写入到文件总结
Mysql查询结果导出/输出/写入到文件 方法一:直接执行命令: mysql> select count(1) from table into outfile '/tmp/test.txt'; ...
- GridView弹出对话框
if (e.Row.RowState == DataControlRowState.Normal || e.Row.RowState == DataControlRowState.Alternate) ...
- ffmpeg默认输出中文为 UTF-8
在使用ffmpeg 进行对音视频文件解码输出信息的时候会出现乱码. 从网上找到了说ffmpeg默认格式 为 utf-8 如果vs工程使用的的 Unicode 则需要将 utf-8转 Unicode 才 ...
- 【328】Python 控制鼠标/键盘+图片识别 综合应用
本文是基于 [267]实现跨网络传数据 的基础上的,由于在弹出 putty 之后,需要手动输入命令(pass.sh.get.sh)来实现数据的传递,另外就是处理完之后需要手动关闭 putty,本文解决 ...
- springboot+shiro+jwt
1.添加依赖 <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-sp ...
- 【Git】三、工作区、暂存区、版本库
一.基础概念 工作区:电脑中可以看到的目录,为电脑中的项目文件 暂存区:暂存修改的地方 版本库:存放项目的各个版本文件 二.详细介绍 工作区为我们工作所使用的目录,在工作区我们对项目文件进行增删改查. ...
- Simple Style
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" x ...
- [udemy]WebDevelopment_Bootstrap,Templates
Bootstrap Introduction Bootstrap 相对于CSS, JS 就像PPT模板相对于PPT 说白了就是前人已经做好了(pre-build)很多模板,你可以直接拿来主义 Boot ...
- Golang之fmt格式“占位符”
golang的fmt包实现了格式化I/O函数: package main import "fmt" type Human struct { Name string } func m ...