关于c#中委托与事件的一些理解
文章目的:作者(初学者)在学习c#的过程中,对事件、委托及其中的“object sender,EventArgs e”一直感觉理解不透,因此在网上找了一些资料,学习并整理出了该篇笔记,希望能将自己的心得记录下来作为积累。限于能力且是初学,有错误的地方还请大家批评指正。
注意:该笔记中有部分内容摘自网上的参考资料(如《浅析c#中的object sender和EventAgs e》),并非作者原创,仅限学习交流,特此声明!
一. 委托与事件
委托与事件是一对相互关联的概念。委托是一种引用类型,可通过声明委托变量,并将其初始化为某个匹配的函数来实现对该函数的引用。事件可以使类或对象向其他类或对象通知发生的相关事情,以使其他类或对象为此做出响应。灵活的事件处理要求将事件的响应分派给相应的事件处理程序(或称事件处理函数、事件处理方法),而C#中事件处理程序通常都是委托实现的。
二. 委托
1. 定义
委托其实是一种引用类型,它通过声明一个委托类型的变量,并通过将该变量初始化一个与该委托具有相同返回值类型和形参列表的函数引用,来实现所谓的对函数的引 用。
2. 声明方式
modifiers(opt) delegate return-Type identifier (fomal-parameter-list(opt))
修饰符(可选) 委托关键字 返回值类型 标识符 (形参列表(可选))
Public delegate double DataProcess (double x, double y)
3. 使用步骤
1)声明委托,指定其返回值类型和形参列表;
2)定义与委托具有相同返回值类型和形参列表的函数;
3) 声明委托类型的变量,并将该委托变量初始化为步骤2)中所定义的与该委托具有相同返回值类型和形参列表的函数引用.
4. 使用举例
public delegate double DataProcess(double x, double y);//声明委托DataProcess,指定其返回值类型和形参列表
class A
{
public static double Multiply(double x, double y); //定义与委托具有相同返回值类型和形参列表的函数Multiply()
{
return x*y;
}
DataProcess dataProcess = new DataProcess(Multiply);//定义了委托之后,声明该委托类型的变量dataProcess,接着把这个变量初始化为与委托具有相同返回值类型和 //形参列表的函数引用Multiply
9 }
三. 事件
1. 定义
1)发起者:引发事件的类或对象;
2) 订户:处理(或称“响应”)事件的类或对象;
3) 事件处理程序:订户提供的一些代码,用于在事件被引发时执行(事件处理程序可位于发起者所在的类,也可位于其他类);
4) 订阅:在发起者引发事件后,执行订户的事件处理程序(即将事件处理程序绑定到事件);
5) 事件:事件由发起者引发,由订户订阅,事件被引发后开始执行订阅该事件的由订户所提供的事件处理程序;
2. 声明方式
modifiers(opt) event Type identifier
修饰符(可选) 委托关键字 类型 标识符
Public event EventHanlder Click
四。 事件的分类及其使用步骤
这里将事件分为两种:1.NetFrameWork类库中已经为我们封装好的事件;2.我们自定义的事件。下面分别说明这两种事件的使用步骤,并给出代码示例:
1.NetFrameWork类库中封装好的事件的使用步骤
注意:在.NET Framwork类库中,事件是基于EventHandler(委托祖类)和EventArgs(事件数据祖类)两个基类的。
1)定义委托
委托的定义用于指定订阅该事件的事件处理程序(或事件处理方法、事件处理函数)的返回值类型及形参列表(此处以backgroundWorker组件为例);
public delegate void DoWorkEventHandler ( object sender, DoWorkEventArgs e);
a. 返回值类型:void
b. 参数1:object sender:
是一个对象,其实是一个对象引用,这里的事件是对象backgroundWorker1的事件,这个对象指的就是backgroundworker1。(backgroundWorker1是B ackgroundWorker类的一个实例)。
c. 参数2:DoWorkEventArgs e:
是一个包含事件数据的对象,用于传递事件的细节,DoWorkEventArgs是一个包含事件数据的类,它继承自包含事件数据的基类EventArgs; 注意:委托的返回值类型和形参列表根据自己的需要定义(包括命名),只要事件处理方法的返回值和参数与之匹配即可。此外委托并不需要与事件位于同一个类或 命名空间中,它可以位于其他类或命名空间,在.Net Framwork的类库中就是这样,自定义事件可根据需要安排。
2)定义事件
public event DoWorkEventHandler DoWork;
该事件位于BackgroundWorker类中,所以调用时为this.backgroundWorker1.DoWork,该事件在RunWorkerAsync()函数中引发,该函数也位于 BackgroundWorker类中。
3)定义监听事件函数(监听事件函数用于在满足一定条件时引发事件)
事件DoWork的监听事件函数是RunWorkerAsync(),该函数已经由.NET在BackgroundWorker类中定义,我们直接用就可以。
4)定义事件处理函数
private void Process(object sender, DoWorkEventArgs e)
{
Console.writeln(“the event has processed!”);
}
注意:要求事件处理函数与委托具有匹配的返回值类型和参数列表。
5)为事件添加事件处理函数
this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(Process);
以上便是.net类库封装的事件的使用过程,但是事件处理函数的添加一般在组件(或控件)的属性中设置即可,另外委托、事件和监听事件函数.NET Framework类 库已经为我们定义好,直接用就可以,我们唯一需要做的就是编写事件处理函数,我们可以在事件处理函数中写入事件发生时我们想做的事情。
2.自定义事件使用步骤(这里以一个类来监听客户端键盘输入事件为例)
1)定义委托
public delegate void UserRequest(Object sender, EventArgs e);
注意:参数Object用来传递事件的发生者,EventArgs用来传递事件的细节(该例暂时没用到)。
2)定义事件 (步骤1)中的委托类型)
public event UserRequest OnUserRequest;
3)定义监听事件函数
监听事件函数用于在满足一定条件时引发事件,监听事件函数对于在.NET Framwork类库中封装的事件已经为我们定义好了,无需我们自己定义。而这里我们需要自 己定义,此函数相当于事件使用步骤1中的RunWorkerAsync()函数
//该函数不断要求用户从键盘输入字符,如果输入结果为“h”,则引发OnUserRequest事件,事件的引发者是本身(this,即该类(或该类实例化对象)),事件细节无(没有传递任何参数给E//ventArgs实例),我们给这个类起名为UserInputMonitor。
public void run()
{
bool finished = false;
do
{
if (Console.ReadLine() == "h")
{
OnUserRequest(this,new EventArgs());//如果用户键盘输入为“h”,则引发事件
}
} while (!finished);
}
4)定义事件处理程序(这里将该函数定义在客户端类(Client类)中)
首先实例化UserInputMonitor类:
UserInputMonitor monitor = new UserInputMonitor();
然后定义事件处理程序
private void ShowMessage(object sender, EventArgs e)
{
Console.WriteLine("haha");//如果用户输入的h,则打印“haha”
}
5)将事件处理程序绑定到事件(或称订阅事件)
这里将其写到Client的构造函数里
Client(UserInputMonitor m)
{
m.OnUserRequest += new UserInputMonitor.UserRequest(this.ShowMessage);
}
接下来创建客户端并开始监听事件
new Client(monitor);//将事件处理方法绑定到事件OnUserRequest
monitor.run();
该自定义事件的全部代码如下所示(c#控制台程序)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace ConsoleApplication2
{ class Client
{
//构造函数
Client(UserInputMonitor m)
{
m.OnUserRequest += new UserInputMonitor.UserRequest(this.ShowMessage);//订阅事件
} public static void Main(string[] args)
{
UserInputMonitor monitor = new UserInputMonitor();
new Client(monitor);//将事件处理方法绑定到事件OnUserRequest
monitor.run();
} private void ShowMessage(object sender, EventArgs e)
{
Console.WriteLine("haha");
}
} class UserInputMonitor
{
public delegate void UserRequest(Object sender, EventArgs e);//定义委托 public event UserRequest OnUserRequest;//定义此委托类型的事件 public void run()
{
bool finished = false;
do
{
if (Console.ReadLine() == "h")
{
OnUserRequest(this,new EventArgs());//如果用户键盘输入为“h”,则事件发生
}
} while (!finished);
} }
}
五. 进一步研究C#中的预定义事件处理机制
可能大家发现在C#中有些事件和前面的似乎不太一样。例如:
private void textBox1_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e)//事件处理程序
{ }
this.textBox1.KeyPress+=newSystem.Windows.Forms.KeyPressEventHandler(this.textBox1_KeyPress);//将事件处理程序绑定到事件KeyPress
这里使用了KeyPressEventArgs而不是EventArgs作为参数。这里使用了KeyEventHandler委托,而不是EventHandler委托。KeyPressEventArgs是EventArgs的派生类,而KeyEventHandler的声明如下 :
public delegate void KeyEventHandler( object sender,KeyEventArgs e);
是参数为KeyEventArgs的委托。那为什么KeyPress事件要这么做呢,我们可以从两个类的构造函数来找答案。
public EventArgs();
public KeyPressEventArgs(char keyChar);
这里的keyChar是什?是用来传递我们按下了哪个键的。我在KeyEventArgs中又发现了如下属性,进一步证明了我的理论。
public char KeyChar { get; }
下面举个具体的例子,加深一下理解:我们也定义一个EventArgs(类似KeyEventArgs)取名MyEventArgs,定义一个构造函数public MyEventArgs(char keyChar),同样我们也设置相应的属性。完成代码如下(控制台程序):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace ConsoleApplication2
{
class MyEventArgs:EventArgs//定义了一个事件类
{
private char keyChar; public MyEventArgs(char keyChar)//构造函数
{
this.keyChar = keyChar;
} public char KeyChar
{
get{return keyChar;}
}
} class UserInputMonitor
{
public delegate void UserRequest(object sender, MyEventArgs e);//定义委托
public event UserRequest OnUserRequest;//定义此委托类型的事件 public void Run()//监听事件函数
{
bool finished = false;
do
{
string inputString = Console.ReadLine();
if(inputString != "")
{
OnUserRequest(this,new MyEventArgs(inputString[]));//引发异常
}
}while(!finished);
}
} class Client
{
static void Main(string[] args)
{
UserInputMonitor monitor = new UserInputMonitor();
new Client(monitor);
monitor.Run(); } Client(UserInputMonitor m)
{
m.OnUserRequest += new UserInputMonitor.UserRequest(this.ShowMessage);//将监听事件函数绑定到事件处理函数 } public void ShowMessage(object sender, MyEventArgs e)
{
Console.WriteLine("捕捉到:{0}",e.KeyChar);
}
}
}
关于c#中委托与事件的一些理解的更多相关文章
- C#中委托和事件的区别实例解析
这篇文章主要介绍了C#中委托和事件的区别,并分别以实例形式展示了通过委托执行方法与通过事件执行方法,以及相关的执行流程与原理分析,需要的朋友可以参考下 本文实例分析了C#中委托和事件的区别,分享给大家 ...
- c#中委托和事件(转)
C# 中的委托和事件 引言 委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易.它们就像是一道槛儿,过了这个槛的人,觉得真 ...
- c#中委托和事件(续)(转)
本文将讨论委托和事件一些更为细节的问题,包括一些大家常问到的问题,以及事件访问器.异常处理.超时处理和异步方法调用等内容. 为什么要使用事件而不是委托变量? 在 C#中的委托和事件 中,我提出了两个为 ...
- 转载:C#中委托、事件与Observer设计模式
原文地址 http://www.tracefact.net/CSharp-Programming/Delegates-and-Events-in-CSharp.aspx 感谢博主分享! 范例说明 假设 ...
- C#中委托和事件
目 录 将方法作为方法的参数 将方法绑定到委托 更好的封装性 限制类型能力 范例说明 Observer 设计模式简介 实现范例的Observer 设计模式 .NET 框架中的委托与事件 为什么委托定义 ...
- C#中委托和事件的区别
大致来说,委托是一个类,该类内部维护着一个字段,指向一个方法.事件可以被看作一个委托类型的变量,通过事件注册.取消多个委托或方法.本篇分别通过委托和事件执行多个方法,从中体会两者的区别. □ 通过委托 ...
- CS中委托与事件的使用-以Winform中跨窗体传值为例
场景 委托(Delegate) 委托是对存有某个方法的引用的一种引用类型变量. 委托特别用于实现事件和回调方法. 声明委托 public delegate int MyDelegate (string ...
- c#中委托和事件区别
委托和事件相同的功能 class Dem5 { public Action deHandler; public event Action eveHa; public Dem5() { deHandle ...
- C#中委托、事件和回调函数的理解
在C#中我们经常会碰到事件,尤其是在WPF或者WinForm中,窗体加载.或者点击一个按钮,都会触发事件.实际上,事件是对委托的封装.如果不进行封装,让委托暴露给调用者,调用者就可以把委托变量重新引用 ...
随机推荐
- os.path等os模块函数
os.path.abspath(path) #返回绝对路径 os.path.basename(path) #返回文件名 os.path.commonprefix(list) #返回list(多个路径) ...
- word 2007 写CSDN博客
目前大部分的博客作者在用Word写博客这件事情上都会遇到以下3个痛点: 1.所有博客平台关闭了文档发布接口,用户无法使用Word,Windows Live Writer等工具来发布博客.使用Word写 ...
- Linux 非阻塞connect,错误码:EINPROGRESS
当我们以非阻塞的方式来进行连接的时候,返回的结果如果是 -1,这并不代表这次连接发生了错误,如果它的返回结果是 EINPROGRESS,那么就代表连接还在进行中. 后面可以通过poll或者select ...
- visual studio 2013 git 记住密码
原有配置: C:\Users\Administrator 下.gitconfig内容为 [user] name = lijf4 email = lijf4@lenovo.com 删除,修改为 [cre ...
- lucene学习笔记(四)lucene分词详解
分词器的核心类 Analyzer SimpleAnalyzer StopAnalyzer WhitespaceAnalyzer StandardAnalyzer TokenStream 分词器做好处理 ...
- ASP.NET添加Mysql数据源
在ASP.NET的数据源控件上添加mysql数据库连接,首先需要在windows系统下添加mysql的数据源才能在vs中添加数据源 1.在控制面板下打开系统与安全-->打开管理工具-->点 ...
- pageadmin CMS网站建设教程:网页设计的常用参数
由于网络速度问题,我们需要考虑图片大小对传输速度的影响,如果图片太大就会影响浏览速度,访问者很快就会对这个网站失去了兴趣,只有充分了解图片质量与下载速度的关系,并了解不同的文件格式,才能更有效的表达内 ...
- C# winform中listview排序
本文解决方案是采用下面链接中的解决方案.十分感谢这篇文章的作者bright:http://blog.163.com/shensc@126/blog/static/1312896522010614103 ...
- scrapy 资料整合
先看看scrapy的框架流程, 1,安装 scrapy 链接 查看即可. 2,新建scrapy项目 scrapy startproject 项目名 目录结构图 3,cd到项目名下,创建任务. scra ...
- LOJ#6544. 杀苍蝇(计算几何)
题面 传送门 题解 枚举一个定点,把剩下的所有点按照极角排序就行了 //minamoto #include<bits/stdc++.h> #define R register #defin ...