面向对象基础——事件和委托

事件和委托  

  在典型的面向对象软件的一般流程中,代码段创建类的对象并在该对象上调用力法。在这种情况下,调用程序是主动代码,因为它们是调用方法的代码。而对象是被动的,因为只有当某种方法被调用时才会用上对象并执行某种动作。

  然而,也可能存在相反的情况。对象可以执行一些任务并在执行过程中发生某些事情时通知调用程序。称这类事情为事件(event),对象的事件发布称为引发事件。

  事件驱动处理对于.NET来说并不是什么技术,在事件驱动处理中,当有事件发生时,某些代码段会通知其他对事件感兴趣的代码段。当用户使用鼠标、敲击键盘或者移动窗口时,Windows用户接口层一直使用事件的形式通知Windows应用程序。当用户采取影响ActiveX控件的动作时,ActiveX控件就会引发事件至ActiveX控件容器。

  为了在C#代码中激发、发布和预约事件更容易,C#语言提供了一些特殊的关键字。使用这些关键字允许C#类毫不费力地激发和处理事件。

1.定义委托

  当设计C#类引发的事件时,需要决定其他代码段如何接收事件。其他代码段需要编写一种方法来接收和处理发布的事件。例如,假设类实现了一个Web服务器,并想在任何时间从Internet发来页面请求时激发一个事件。在类激发这个new request事件时,其他代码段执行某种动作,并且代码中应该包含一种方法,在激发事件时执行该方法。

  类用户实现接受和处理事件的方法由C#中的概念——委托(delegate)定义。委托是一种“函数模板”,它描述了用户的事件处理程序必须有的结构。委托也是一个类,其中包含一个签名以及对方法的引用。就像一个函数指针,但是它又能包含对静态和实例方法的引用。对于实例方法来说,委托存储了对函数人口点的引用以及对对象的引用。委托定义了用户事件处理程序内该返回的内容以及应该具备的参数表。

  要在C#中定义一个委托,使用下列语法:

delegate 事件处理程序的返回类型 委托标识符(事件处理程序的参数表)

如果在激发事件的类中声明委托,可以在委托前加上前缀public,protected,internal或者 private关键字,如下面的delegate定义示例所示:

public delegate void EvenNumberHandler(int Number);

在上述示例中,创建了一个称为EvenNumberHandler的公共委托(public delegate),它不返回任何值。该委托只定义了一个要传递给它的参数,该参数为int类型。委托标识符(这里是EvenNumberHandler)可以是任何名称,只要不与C#关键字的名称重复即可。

2.定义事件

为了阐述清楚事件的概念,我们以一个示例开始讲述。假设正驱车在路上并且仪表板上显示燃料不足的灯亮了。在这个过程中。汽缸中的传感器给计算机发出燃料快要耗尽的信号。然后,计算机又激发一个事件来点亮仪表板上的灯,这样司机就知道要购买更多的油了。用最简单的话来说,事件是计算机警告你发发生某种状况的一种方式

  使用C#的关键字event来定义类激发的事件。在C#中事件声明的最简单形式包含下列内容:

evet 事件类型 事件标识符 事件类型委托标识符匹配

以下面的Web服务器示例为例:

public delegate void NewRequestHandler(string URL);

public class WebSever

{

public event NewRequestHandler NewRequestEvent;

//...

}

  上述示例声明了一个称为NewRequestHandler的委托。NewRequestHandler定义了一个委托,作为处理new request事件的方法的方法模板。任何处理new request事件的方法都必须遵循委托的调用规则:必须不返回任何数据,必须用一个字符串作为参数表。事件处理程序的实现可以拥有任意的方法名称,只要返回值和参数表符合委托模板的要求即可。

  WebServer类定义了一个事件NewRequestEvent。该事件的类型为NewRequestHandle。这意味着,只有与该委托的调用规则相匹配的事件处理程序才可以用于处理NewRequestEvent事件。

3.安装事件

编写完事件处理程序之后,必须用new运算符创建一个它的实例并将它安装到激发事件的类中。创建一个事件处理程序new实例时,要用new运算符创建一个属于这种委托类型的变量,并且作为参数传递事件处理程序方法的名称。

  以Web服务器的示例为例,对事件处理程序new实例的创建如下代码所示:

public void MyNewRequestHandler(string URL)

{

}

NewRequestHandler HandlerInstance;

HandlerInstance = new NewRequestHandler(MyNewRequestHandler);

  在创建了事件处理程序的new实例之后,使用+=运算符将其添加到事件变量中:

NewRequestEvent += HandlerInstance;

  上述语句连接了HandleInstance委托实例与NewRequestEvent事件,该委托支持MyNewRequestMethod方法。使用+=运算符,可以将任意多的委托实例与一个事件相连接。同理,可以使用-=运算符从事件连接中删除委托实例:

NewRequestEvent -= HandlerInstance;

上述语句解除了HandlerInstance委托实例与NewRequestEvent事件的连接。

4.激发事件

  可以使用事件标识符(比如事件的名称)从类中激发事件.就像事件是一个方法一样。作为方法调用事件将激发该事件c在Web浏览器示例中,使用如下语句激发new request事件:

NewRequestEvent(strURLOfNewRequest);

  事件激发调用中使用的参数必须与事件委托的参数表匹配。定义NewRequestEvent事件的委托接受一个字符串参数;因此,当从Web浏览器类中激发该事件时,必须提供一个字符串。

  

  下面的例子说明了委托和事件的概念。其中实现了一个类,该类从0计数到100,并在计数过程中找到偶数时激发一个事件。

示例:

using System;

public delegate void EvenNumberHandler(int Number);

class Counter

{

public event EvenNumberHandler OnEvenNumber;

public Counter()

{

OnEvenNumber = null;

}

public void CountTo100()

{

int CurrentNumber;

for(CurrentNumber = 0; CurrentNumber <= 100;CurrentNumber++)

{

if(CurrentNumber % 2 == 0)

{

if(OnEvenNumber != null)

{

 OnEventNumber(CurrentNumber);

}

}

}

}

}

classEvenNumberHandlerClass

{

public void EvenNumberFound(int EvenNumber)

{

Console.WriteLine(EvenNumber);

}

}

class ClassMain

{

public static void Main()

{

Counter MyCounter = new Counter();

EvenNumberHandlerClass MyEvenNumberHandlerClass = new

EvenNumberHandlerClass();

MyCounter.OnEvenNUmber += new

EvenNumberHandler(MyEvenNumberHandlerClass.EvenNumberFound);

}

}

程序实现了三个类

● Counter类执行计数功能。其中实现了一个公共方法CountTo100()和一个公共事件    OnEvenNumber。OnEvenNumber事件的委托类型为EvenNumberHandler。

● EvenNumherHandlerClass类包含一个公共方法EvenNumberFound。该方法为Counter类  的OnEvenNumber事件的事件处理程序。它将作为参数提供的整数打印到控制台上。

● MainClass类包含应用程序的Main()方法。Main()方法创建类Counter的一个对象并将该对象命名为MyCounter。还创建了类EvenNumberHandlerC1ass的一个new对象,井调用了对象MyEvenNumberHandlerC1ass。Majn()方法调用MyCounter对象的CountTo100()方法,但是不是在将委托实例安装到Counter类中之前调用的。其中的代码创建了一个new委托实例,用它来管理MyEvenNumber。HandlerClass对象的EvenNumberFound方法,并使用+=运算法将其添加到MyCounter对象的0nEvenNumber事件中。

  CountTol00方法的实现使用一个局部变量从0计数到100。在每一次计数循环中,代码都会检测数字是否是偶数,方法是看数字被2除后是否有余数。如果数字确实是偶数,代码就激发OnEvenNumber事件,将偶数作为参数提供,以便与事件委托的参数表匹配。

  因为MyEvenNumherHandlerClass的EvenNumberFound方法是作为事件处理程序安装的,而且该方法将提供的参数打印到控制台上,所以编译并运行代码后,0到100之间的所有偶数都会打印到控制台上。

5.标准化事件的设计

  尽管C#可以接受它编译的任何委托设计,但是.NET框架还是鼓励开发人员采用标准的委托设计方式。

  最好委托设计使用两个参数;例如,SystemEventhandler委托:

● 对引发事件的对象的引用

● 包含与事件有关的数据的对象

  第二个参数包含了所有事件数据,应该是某个类的一个对象.这个类由.NET的System.EventArgs类派生出来。

  

  对上面代码进行修改,其中使用了这种标准的设计方式。

示例:

using System;

public delegate void EvenNumberHandler(object Originator,

OnEvenNumberEventArgs EventNumberEventArgs);

class Counter

{

public event EvenNumberHandler OnEvenNumber;

public Counter()

{

OnEvenNumber = null;

}

public void CountTo100()

{

int CurrentNumber;

for(CurrentNumber = 0; CurrentNumber <= 100;CurrentNumber++)

{

if(CurrentNumber % 2 == 0)

{

if(OnEvenNumber != null)

{

 OnEvenNumberEventArgs EventArguments;

 EventArguments = new OnEvenNumberEvenArgs(CurrentNumber);

 OnEventNumber(this,EventArguments);

}

}

}

}

}

public class OnEvenNumberEventArgs : EventArgs

{

private int EventNumber;

public OnEvenNumberEventArgs(int EvenNumber)

{

this.EvenNumber = EvenNumber;

}

public int Number

{

get

{

return EventNumber;

}

}

}

class EvenNumberHandlerClass

{

public void EvenNumberFound(object Originator,

OnEvenNumberEventArgs EvenNumberEventArgs)

{

Console.WriteLine(EvenNumberEventArgs.Number);

}

}

Class MainClass

{

public static void Main()

{

Counter MyCounter = new Counter();

EvenNumberHandlerClass MyEvenNumberHandlerClass = new

EvenNumberHandlerClass();

MyCounter.OnEvenNUmber += new

EvenNumberHandler(MyEvenNumberHandlerClass.EvenNumberFound);

MyCounter.CountTo100();

}

}
时间: 2024-10-05 20:12:39

面向对象基础——事件和委托的相关文章

C#基础知识之事件和委托

本文中,我将通过两个范例由浅入深地讲述什么是委托.为什么要使用委托.事件的由来..Net Framework中的委托和事件.委托和事件对Observer设计模式的意义,对它们的中间代码也做了讨论. 委托的引入:将方法作为方法的参数 我们先不管这个标题如何的绕口,也不管委托究竟是个什么东西,来看下面这两个最简单的方法,它们不过是在屏幕上输出一句问候的话语: public void GreetPeople(string name) { EnglishGreeting(name); } public

面向对象——一起来复习委托与事件!

事件与委托其实并不难理解,只是因为它们的使用方式与常用的编码有很大的差别,例如通常编写的都是同步代码,调用一个类型的方法,会即刻出现方法执行的结果,这是符合逻辑的.但在某些情况中,同步代码未必满足需求,拿最近的打车软件打个比方,搭车者发送一个信息,就会推送给附近的司机,使用同步代码,搭车对象肯定需要调用司机中心对象,这样就出现了我们一直不愿意看到的情况:两个类型紧密地耦合在一起.既然要其它类型对自己的行为作出反应,亲自调用其类型的方法似乎不可避免,在同步代码中,很难避免这种紧密的类型调用关系.而

【Unity|C#】基础篇(8)——委托(Delegate)/ 事件(Event)

[学习资料] > 在线文档 官方文档:https://docs.microsoft.com/zh-cn/dotnet/csharp/ 菜鸟教程(高级教程):https://www.runoob.com/csharp/csharp-tutorial.html > 视频教程 腾讯学院.Siki学院         > 书籍 <C#图解教程>(第13~14章):https://www.cnblogs.com/moonache/p/7687551.html [学习内容] > 菜

事件与委托

C# 中的委托和事件 文中代码在VS2005下通过,由于VS2003(.Net Framework 1.1)不支持隐式的委托变量,所以如果在一个接受委托类型的位置直接赋予方法名,在VS2003下会报错,解决办法是显式的创建一个委托类型的实例(委托变量).例如:委托类型 委托实例 = new 委托类型(方法名); 引言 委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易.它们就像是一道槛儿,过了这个槛的人,觉得真是太容易

大话设计模式学习笔记——面向对象基础

前言 好记性不如烂"笔头"系列--大话设计模式学习笔记 目录 面向对象基础 面向对象基础 什么是类与实例 一切事物皆为对象,即所有的东西老师对象,对象就是可以看到.感觉到.听到.触摸到.尝到.或闻到的东西.准确地说,对象是一个自包含的实体,用一组可识别的特性和行为来标识.面向对象编程,英文叫 Object-Oriented Programming,其实就是针对对象来进行编程的意思.类就是具有相同属性和功能的对象的抽象集合.实例就是一个真实的对象.比如我们属于'人'类,而个人就是'人'类

【设计模式】面向对象基础

最近一直在学习<大话设计模式>根据师傅对我的指导,让我先学习这本书的附录--面向对象基础.说实在话,面向对象技术从很早就接触了,从刚开始的C++,其次是软工视频,然后是UML,紧接着是C#,直到现在的设计模式.. 总的来说,在设计模式之前,我对面向对象的了解还只是停留在封装.继承.多态的层次上,虽然在C#视频中,有全面的讲解了一下,但是自己还是晕晕乎乎的,脑子中还是有很多疑云..直到这次的设计模式,通过通过幽默生动.而又通俗易懂的故事,对面向对象技术深入浅出,我才算是真正的入门了.. 这篇博客

C#事件与委托详解【精华 多看看】

Delegate delegate是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类.与其它的类不同,delegate类能够拥有一个签名(signature),并且它"只能持有与它的签名相匹配的方法的引用".它所实现的功能与C/C++中的函数指针十分相似.它允许你传递一个类A的方法m给另一个类B的对象,使得类B的对象能够调用这个方法m.但与函数指针相比,delegate有许多函数委托和事件在 .Net Framework中的应用非常广泛指针不具备的优点.首先,函数指针只能指

[WuDe]C#程序设计教程 - 第2章 C#面向对象基础

第2章 C#面向对象基础 2.1 类 类是一种数据类型,而对象是具有这种类型的变量 [类的修饰符] class 类名 [:基类名] { //类的成员 }[;] 访问级别的用处在于控制成员在哪些地方可以被访问,这样达到面向对象中"封装"的目的;控制对外访问权限 在类这个级别,不写访问修饰符默认为internal,类只有两个访问修饰符,public 和internal (暂时这样理解),在类里面,方法外定义变量或方法前不加访问修饰符,默认为private Public公共类:不受限制对该类

C#基础---事件的使用

一:什么是事件     事件是可以被控件识别的操作,如按下确定按钮,选择某个单选按钮或者复选框.每一种控件有自己可以识别的事件,如窗体的加载.单击.双击等事件,编辑框(文本框)的文本改变事件,等等.事件在桌面应用程序里面无处可见,比如winform,WPF...,其次事件是基于委托而产生的. 二:事件的基本使用 1.事件的声明: 其实和委托一样只是多了一个Event而已.ShowMsg 就具备了ShowMsgHandler的功能. Notes: 1. 委托可以依赖于一个类或者一个域名空间(C#基