委托与事件的区别

委托与事件的区别
委托与事件的区别

委托与事件的区别

委托和事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#

时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没

有过去的人每次见到委托和事件就觉得心里别(biè)得慌,混身不自在。本文中,我将通过两个

范例由浅入深地讲述什么是委托、为什么要使用委托、事件的由来、.Net Framework中的委托和

事件、委托和事件对Observer设计模式的意义,对它们的中间代码也做了讨论。

将方法作为方法的参数

我们先不管这个标题如何的绕口,也不管委托究竟是个什么东西,来看下面这两个最简单的方法,它们不过是在屏幕上输出一句问候的话语:

public void GreetPeople(string name) {

// 做某些额外的事情,比如初始化之类,此处略

EnglishGreeting(name);

}

public void EnglishGreeting(string name) {

Console.WriteLine("Morning, " + name);

}

暂且不管这两个方法有没有什么实际意义。GreetPeople用于向某人问好,当我们传递代表某人姓

名的name参数,比如说“Jimmy”,进去的时候,在这个方法中,将调用EnglishGreeting方法,再

次传递name参数,EnglishGreeting则用于向屏幕输出“Morning, Jimmy”。

现在假设这个程序需要进行全球化,哎呀,不好了,我是中国人,我不明白“Morning”是什么意思,怎么办呢?好吧,我们再加个中文版的问候方法:

public void ChineseGreeting(string name){

Console.WriteLine("早上好, " + name);

}

这时候,GreetPeople也需要改一改了,不然如何判断到底用哪个版本的Greeting问候方法合适呢?在进行这个之前,我们最好再定义一个枚举作为判断的依据:

public enum Language{

English, Chinese

}

public void GreetPeople(string name, Language lang){

//做某些额外的事情,比如初始化之类,此处略

swith(lang){

case Language.English:

EnglishGreeting(name);

break;

case Language.Chinese:

ChineseGreeting(name);

break;

}

}

OK,尽管这样解决了问题,但我不说大家也很容易想到,这个解决方案的可扩展性很差,如果日

后我们需要再添加韩文版、日文版,就不得不反复修改枚举和GreetPeople()方法,以适应新的需求。

在考虑新的解决方案之前,我们先看看 GreetPeople的方法签名:

public void GreetPeople(string name, Language lang)

我们仅看 string name,在这里,string 是参数类型,name 是参数变量,当我们赋给name字符串“jimmy”时,它就代表“jimmy”这个值;当我们赋给它“张子阳”时,它又代表着“张子阳”这个值。然后,我们可以在方法体内对这个name进行其他操作。哎,这简直是废话么,刚学程序就知道了。

如果你再仔细想想,假如GreetPeople()方法可以接受一个参数变量,这个变量可以代表另一个方法,当我们给这个变量赋值 EnglishGreeting的时候,它代表着 EnglsihGreeting() 这个方法;当我们

给它赋值ChineseGreeting 的时候,它又代表着ChineseGreeting()方法。我们将这个参数变量命名

为 MakeGreeting,那么不是可以如同给name赋值时一样,在调用 GreetPeople()方法的时候,给

这个MakeGreeting 参数也赋上值么(ChineseGreeting或者EnglsihGreeting等)?然后,我们在方法

体内,也可以像使用别的参数一样使用MakeGreeting。但是,由于MakeGreeting代表着一个方法,它的使用方式应该和它被赋的方法(比如ChineseGreeting)是一样的,比如:

MakeGreeting(name);

好了,有了思路了,我们现在就来改改GreetPeople()方法,那么它应该是这个样子了:

public void GreetPeople(string name, *** MakeGreeting){

MakeGreeting(name);

}

注意到 *** ,这个位置通常放置的应该是参数的类型,但到目前为止,我们仅仅是想到应该有个

可以代表方法的参数,并按这个思路去改写GreetPeople方法,现在就出现了一个大问题:这个

代表着方法的MakeGreeting参数应该是什么类型的?

NOTE:这里已不再需要枚举了,因为在给MakeGreeting赋值的时候动态地决定使用哪个方法,

是ChineseGreeting还是 EnglishGreeting,而在这个两个方法内部,已经对使用“morning”还是“早上好”作了区分。

聪明的你应该已经想到了,现在是委托该出场的时候了,但讲述委托之前,我们再看看MakeGreeting参数所能代表的 ChineseGreeting()和EnglishGreeting()方法的签名:

public void EnglishGreeting(string name)

public void ChineseGreeting(string name)

如同name可以接受String类型的“true”和“1”,但不能接受bool类型的true和int类型的1一样。MakeGreeting的参数类型定义应该能够确定 MakeGreeting可以代表的方法种类,再进一步讲,

就是MakeGreeting可以代表的方法的参数类型和返回类型。

于是,委托出现了:它定义了MakeGreeting参数所能代表的方法的种类,也就是MakeGreeting

参数的类型。

NOTE:如果上面这句话比较绕口,我把它翻译成这样:string 定义了name参数所能代表的值的

种类,也就是name参数的类型。

本例中委托的定义:

public delegate void GreetingDelegate(string name);

可以与上面EnglishGreeting()方法的签名对比一下,除了加入了delegate关键字以外,其余的是不

是完全一样?

现在,让我们再次改动GreetPeople()方法,如下所示:

public void GreetPeople(string name, GreetingDelegate MakeGreeting){

MakeGreeting(name);

}

如你所见,委托GreetingDelegate出现的位置与 string相同,string是一个类型,那么GreetingDelegate应该也是一个类型,或者叫类(Class)。但是委托的声明方式和类却完全不同,这

是怎么一回事?实际上,委托在编译的时候确实会编译成类。因为Delegate是一个类,所以在任

何可以声明类的地方都可以声明委托。更多的内容将在下面讲述,现在,请看看这个范例的完整

代码:

using System;

using System.Collections.Generic;

using System.Text;

namespace Delegate {

//定义委托,它定义了可以代表的方法的类型

public delegate void GreetingDelegate(string name);

class Program {

private static void EnglishGreeting(string name) {

Console.WriteLine("Morning, " + name);

}

private static void ChineseGreeting(string name) {

Console.WriteLine("早上好, " + name);

}

//注意此方法,它接受一个GreetingDelegate类型的方法作为参数

private static void GreetPeople(string name, GreetingDelegate MakeGreeting) {

MakeGreeting(name);

}

static void Main(string[] args) {

GreetPeople("Jimmy Zhang", EnglishGreeting);

GreetPeople("张子阳", ChineseGreeting);

Console.ReadKey();

}

}

}

输出如下:

Morning, Jimmy Zhang

早上好, 张子阳

我们现在对委托做一个总结:

委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递,这种将方法动态地赋给参数的做法,可以避免在程序中大量使用If-Else(Switch)语句,同时使得程序具有更好的可扩展性。

将方法绑定到委托

看到这里,是不是有那么点如梦初醒的感觉?于是,你是不是在想:在上面的例子中,我不一定要直接在GreetPeople()方法中给 name参数赋值,我可以像这样使用变量:

static void Main(string[] args) {

string name1, name2;

name1 = "Jimmy Zhang";

name2 = "张子阳";

GreetPeople(name1, EnglishGreeting);

GreetPeople(name2, ChineseGreeting);

Console.ReadKey();

}

而既然委托GreetingDelegate 和类型 string 的地位一样,都是定义了一种参数类型,那么,我是不是也可以这么使用委托?

static void Main(string[] args) {

GreetingDelegate delegate1, delegate2;

delegate1 = EnglishGreeting;

delegate2 = ChineseGreeting;

GreetPeople("Jimmy Zhang", delegate1);

GreetPeople("张子阳", delegate2);

Console.ReadKey();

}

如你所料,这样是没有问题的,程序一如预料的那样输出。这里,我想说的是委托不同于string 的一个特性:可以将多个方法赋给同一个委托,或者叫将多个方法绑定到同一个委托,当调用这个委托的时候,将依次调用其所绑定的方法。在这个例子中,语法如下:

static void Main(string[] args) {

GreetingDelegate delegate1;

delegate1 = EnglishGreeting; // 先给委托类型的变量赋值

delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法

// 将先后调用 EnglishGreeting 与 ChineseGreeting 方法

GreetPeople("Jimmy Zhang", delegate1);

Console.ReadKey();

}

输出为:

Morning, Jimmy Zhang

早上好, Jimmy Zhang

实际上,我们可以也可以绕过GreetPeople方法,通过委托来直接调用EnglishGreeting和ChineseGreeting:

static void Main(string[] args) {

GreetingDelegate delegate1;

delegate1 = EnglishGreeting; // 先给委托类型的变量赋值

delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法

// 将先后调用 EnglishGreeting 与 ChineseGreeting 方法

delegate1 ("Jimmy Zhang");

Console.ReadKey();

}

NOTE:这在本例中是没有问题的,但回头看下上面GreetPeople()的定义,在它之中可以做一些对于EnglshihGreeting和ChineseGreeting来说都需要进行的工作,为了简便我做了省略。

注意这里,第一次用的“=”,是赋值的语法;第二次,用的是“+=”,是绑定的语法。如果第一次就使用“+=”,将出现“使用了未赋值的局部变量”的编译错误。

我们也可以使用下面的代码来这样简化这一过程:

GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);

delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法

看到这里,应该注意到,这段代码第一条语句与实例化一个类是何其的相似,你不禁想到:上面第一次绑定委托时不可以使用“+=”的编译错误,或许可以用这样的方法来避免:

GreetingDelegate delegate1 = new GreetingDelegate();

delegate1 += EnglishGreeting; // 这次用的是“+=”,绑定语法。

delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法

但实际上,这样会出现编译错误:“GreetingDelegate”方法没有采用“0”个参数的重载。尽管这样的结果让我们觉得有点沮丧,但是编译的提示:“没有0个参数的重载”再次让我们联想到了类的构造函数。我知道你一定按捺不住想探个究竟,但再此之前,我们需要先把基础知识和应用介绍完。

既然给委托可以绑定一个方法,那么也应该有办法取消对方法的绑定,很容易想到,这个语法是“-=”:

static void Main(string[] args) {

GreetingDelegate delegate1 = new GreetingDelegate(EnglishGreeting);

delegate1 += ChineseGreeting; // 给此委托变量再绑定一个方法

// 将先后调用 EnglishGreeting 与 ChineseGreeting 方法

GreetPeople("Jimmy Zhang", delegate1);

Console.WriteLine();

delegate1 -= EnglishGreeting; //取消对EnglishGreeting方法的绑定

// 将仅调用 ChineseGreeting

GreetPeople("张子阳", delegate1);

Console.ReadKey();

}

输出为:

Morning, Jimmy Zhang

早上好, Jimmy Zhang

早上好, 张子阳

让我们再次对委托作个总结:

使用委托可以将多个方法绑定到同一个委托变量,当调用此变量时(这里用“调用”这个词,是因为

此变量代表一个方法),可以依次调用所有绑定的方法。

事件的由来

我们继续思考上面的程序:上面的三个方法都定义在Programe类中,这样做是为了理解的方便,实际应用中,通常都是 GreetPeople 在一个类中,ChineseGreeting和 EnglishGreeting 在另外的类中。现在你已经对委托有了初步了解,是时候对上面的例子做个改进了。假设我们将GreetingPeople()

放在一个叫GreetingManager的类中,那么新程序应该是这个样子的:

namespace Delegate {

//定义委托,它定义了可以代表的方法的类型

public delegate void GreetingDelegate(string name);

//新建的GreetingManager类

public class GreetingManager{

public void GreetPeople(string name, GreetingDelegate MakeGreeting) {

MakeGreeting(name);

}

}

class Program {

private static void EnglishGreeting(string name) {

Console.WriteLine("Morning, " + name);

}

private static void ChineseGreeting(string name) {

Console.WriteLine("早上好, " + name);

}

static void Main(string[] args) {

// ... ...

}

}

}

这个时候,如果要实现前面演示的输出效果,Main方法我想应该是这样的:

static void Main(string[] args) {

GreetingManager gm = new GreetingManager();

gm.GreetPeople("Jimmy Zhang", EnglishGreeting);

gm.GreetPeople("张子阳", ChineseGreeting);

}

我们运行这段代码,嗯,没有任何问题。程序一如预料地那样输出了:

Morning, Jimmy Zhang

早上好, 张子阳

现在,假设我们需要使用上一节学到的知识,将多个方法绑定到同一个委托变量,该如何做呢?让我们再次改写代码:

static void Main(string[] args) {

GreetingManager gm = new GreetingManager();

GreetingDelegate delegate1;

delegate1 = EnglishGreeting;

delegate1 += ChineseGreeting;

gm.GreetPeople("Jimmy Zhang", delegate1);

}

输出:

Morning, Jimmy Zhang

早上好, Jimmy Zhang

到了这里,我们不禁想到:面向对象设计,讲究的是对象的封装,既然可以声明委托类型的变量(在上例中是delegate1),我们何不将这个变量封装到 GreetManager类中?在这个类的客户端中使用不是更方便么?于是,我们改写GreetManager类,像这样:

public class GreetingManager{

//在GreetingManager类的内部声明delegate1变量

public GreetingDelegate delegate1;

public void GreetPeople(string name, GreetingDelegate MakeGreeting) {

MakeGreeting(name);

}

}

现在,我们可以这样使用这个委托变量:

static void Main(string[] args) {

GreetingManager gm = new GreetingManager();

gm.delegate1 = EnglishGreeting;

gm.delegate1 += ChineseGreeting;

gm.GreetPeople("Jimmy Zhang", gm.delegate1);

}

尽管这样达到了我们要的效果,但是似乎并不美气,光是第一个方法注册用“=”,第二个用“+=”就让人觉得别扭。此时,轮到Event出场了,C# 中可以使用事件来专门完成这项工作,我们改写GreetingManager类,它变成了这个样子:

public class GreetingManager{

//这一次我们在这里声明一个事件

public event GreetingDelegate MakeGreet;

public void GreetPeople(string name, GreetingDelegate MakeGreeting) {

MakeGreeting(name);

}

}

很容易注意到:MakeGreet 事件的声明与之前委托变量delegate1的声明唯一的区别是多了一个event关键字。看到这里,你差不多明白到:事件其实没什么不好理解的,声明一个事件不过类似于声明一个委托类型的变量而已。

我们想当然地改写Main方法:

static void Main(string[] args) {

GreetingManager gm = new GreetingManager();

gm.MakeGreet = EnglishGreeting; // 编译错误1

gm.MakeGreet += ChineseGreeting;

gm.GreetPeople("Jimmy Zhang", gm.MakeGreet); //编译错误2

}

这次,你会得到编译错误:事件“Delegate.GreetingManager.MakeGreet”只能出现在 += 或 -= 的左边(从类型“Delegate.GreetingManager”中使用时除外)。

事件和委托的编译代码

这时候,我们不得不注释掉编译错误的行,然后重新进行编译,再借助Reflactor来对 event的声明语句做一探究,看看为什么会发生这样的错误:

public event GreetingDelegate MakeGreet;

可以看到,实际上尽管我们在GreetingManager里将 MakeGreet 声明为public,但是,实际上MakeGreet会被编译成私有字段,难怪会发生上面的编译错误了,因为它根本就不允许在GreetingManager类的外面以赋值的方式访问。

我们进一步看下MakeGreet所产生的代码:

private GreetingDelegate MakeGreet; //对事件的声明实际是声明一个私有的委托变量[MethodImpl(MethodImplOptions.Synchronized)]

public void add_MakeGreet(GreetingDelegate value){

this.MakeGreet = (GreetingDelegate) https://www.360docs.net/doc/9c4467024.html,bine(this.MakeGreet, value);

[MethodImpl(MethodImplOptions.Synchronized)]

public void remove_MakeGreet(GreetingDelegate value){

this.MakeGreet = (GreetingDelegate) Delegate.Remove(this.MakeGreet, value);

}

现在已经很明确了:MakeGreet 事件确实是一个GreetingDelegate类型的委托,只不过不管是不是声明为public,它总是被声明为private。另外,它还有两个方法,分别是add_MakeGreet和remove_MakeGreet,这两个方法分别用于注册委托类型的方法和取消注册,实际上也就是:“+= ”对应 add_MakeGreet,“-=”对应remove_MakeGreet。而这两个方法的访问限制取决于声明事件时的访问限制符。

在add_MakeGreet()方法内部,实际上调用了System.Delegate的Combine()静态方法,这个方法用于将当前的变量添加到委托链表中。我们前面提到过两次,说委托实际上是一个类,在我们定义委托的时候:

public delegate void GreetingDelegate(string name);

当编译器遇到这段代码的时候,会生成下面这样一个完整的类:

public class GreetingDelegate:System.MulticastDelegate{

public GreetingDelegate(object @object, IntPtr method);

public virtual IAsyncResult BeginInvoke(string name, AsyncCallback callback, object @object);

public virtual void EndInvoke(IAsyncResult result);

public virtual void Invoke(string name);

}

关于这个类的更深入内容,可以参阅《CLR Via C#》等相关书籍,这里就不再讨论了。

委托、事件与Observer设计模式

范例说明

上面的例子已不足以再进行下面的讲解了,我们来看一个新的范例,因为之前已经介绍了很多的内容,所以本节的进度会稍微快一些:

假设我们有个高档的热水器,我们给它通上电,当水温超过95度的时候:1、扬声器会开始发出语音,告诉你水的温度;2、液晶屏也会改变水温的显示,来提示水已经快烧开了。

现在我们需要写个程序来模拟这个烧水的过程,我们将定义一个类来代表热水器,我们管它叫:Heater,它有代表水温的字段,叫做temperature;当然,还有必不可少的给水加热

方法BoilWater(),一个发出语音警报的方法MakeAlert(),一个显示水温的方法,ShowMsg()。

namespace Delegate {

class Heater {

private int temperature; // 水温

// 烧水

public void BoilWater() {

for (int i = 0; i <= 100; i++) {

temperature = i;

if (temperature > 95) {

MakeAlert(temperature);

ShowMsg(temperature);

}

}

}

// 发出语音警报

private void MakeAlert(int param) {

Console.WriteLine("Alarm:嘀嘀嘀,水已经 {0} 度了:" , param);

}

// 显示水温

private void ShowMsg(int param) {

Console.WriteLine("Display:水快开了,当前温度:{0}度。" , param);

}

}

class Program {

static void Main() {

Heater ht = new Heater();

ht.BoilWater();

}

}

}

Observer设计模式简介

上面的例子显然能完成我们之前描述的工作,但是却并不够好。现在假设热水器由三部分组成:热水器、警报器、显示器,它们来自于不同厂商并进行了组装。那么,应该是热水器仅仅负责烧水,它不能发出警报也不能显示水温;在水烧开时由警报器发出警报、显示器显示提示和水温。

这时候,上面的例子就应该变成这个样子:

// 热水器

public class Heater {

private int temperature;

// 烧水

private void BoilWater() {

for (int i = 0; i <= 100; i++) {

temperature = i;

}

}

}

// 警报器

public class Alarm{

private void MakeAlert(int param) {

Console.WriteLine("Alarm:嘀嘀嘀,水已经 {0} 度了:" , param);

}

}

// 显示器

public class Display{

private void ShowMsg(int param) {

Console.WriteLine("Display:水已烧开,当前温度:{0}度。" , param);

}

}

这里就出现了一个问题:如何在水烧开的时候通知报警器和显示器?在继续进行之前,我们先了解一下Observer设计模式,Observer设计模式中主要包括如下两类对象:

Subject:监视对象,它往往包含着其他对象所感兴趣的内容。在本范例中,热水器就是一

个监视对象,它包含的其他对象所感兴趣的内容,就是temprature字段,当这个字段的值

快到100时,会不断把数据发给监视它的对象。

Observer:监视者,它监视Subject,当Subject中的某件事发生的时候,会告知Observer,而Observer则会采取相应的行动。在本范例中,Observer有警报器和显示器,它们采取的行动分别是发出警报和显示水温。

在本例中,事情发生的顺序应该是这样的:

1. 警报器和显示器告诉热水器,它对它的温度比较感兴趣(注册)。

2. 热水器知道后保留对警报器和显示器的引用。

3. 热水器进行烧水这一动作,当水温超过95度时,通过对警报器和显示器的引用,自动

调用警报器的MakeAlert()方法、显示器的ShowMsg()方法。

类似这样的例子是很多的,GOF对它进行了抽象,称为Observer设计模式:Observer设

计模式是为了定义对象间的一种一对多的依赖关系,以便于当一个对象的状态改变时,其他依赖于它的对象会被自动告知并更新。Observer模式是一种松耦合的设计模式。

实现范例的Observer设计模式

我们之前已经对委托和事件介绍很多了,现在写代码应该很容易了,现在在这里直接给出代码,并在注释中加以说明。

using System;

using System.Collections.Generic;

using System.Text;

namespace Delegate {

// 热水器

public class Heater {

private int temperature;

public delegate void BoilHandler(int param); //声明委托

public event BoilHandler BoilEvent; //声明事件

// 烧水

public void BoilWater() {

for (int i = 0; i <= 100; i++) {

temperature = i;

if (temperature > 95) {

if (BoilEvent != null) { //如果有对象注册

BoilEvent(temperature); //调用所有注册对象的方法

}

}

}

}

}

// 警报器

public class Alarm {

public void MakeAlert(int param) {

Console.WriteLine("Alarm:嘀嘀嘀,水已经 {0} 度了:", param);

}

}

// 显示器

public class Display {

public static void ShowMsg(int param) { //静态方法

Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", param); }

}

class Program {

static void Main() {

Heater heater = new Heater();

Alarm alarm = new Alarm();

heater.BoilEvent += alarm.MakeAlert; //注册方法

heater.BoilEvent += (new Alarm()).MakeAlert; //给匿名对象注册方法

heater.BoilEvent += Display.ShowMsg; //注册静态方法

heater.BoilWater(); //烧水,会自动调用注册过对象的方法

}

}

}

输出为:

Alarm:嘀嘀嘀,水已经 96 度了:

Alarm:嘀嘀嘀,水已经 96 度了:

Display:水快烧开了,当前温度:96度。

// 省略...

.Net Framework中的委托与事件

尽管上面的范例很好地完成了我们想要完成的工作,但是我们不仅疑惑:为什么.Net Framework 中的事件模型和上面的不同?为什么有很多的EventArgs参数?

在回答上面的问题之前,我们先搞懂 .Net Framework的编码规范:

1. 委托类型的名称都应该以EventHandler结束。

2. 委托的原型定义:有一个void返回值,并接受两个输入参数:一个Object 类型,一个EventArgs类型(或继承自EventArgs)。

3. 事件的命名为委托去掉 EventHandler之后剩余的部分。

4. 继承自EventArgs的类型应该以EventArgs结尾。

再做一下说明:

1.委托声明原型中的Object类型的参数代表了Subject,也就是监视对象,在本例中是Heater(热水器)。回调函数(比如Alarm的MakeAlert)可以通过它访问触发事件的对象(Heater)。

2.EventArgs 对象包含了Observer所感兴趣的数据,在本例中是temperature。

上面这些其实不仅仅是为了编码规范而已,这样也使得程序有更大的灵活性。比如说,如果我们不光想获得热水器的温度,还想在Observer端(警报器或者显示器)方法中获得它的生产日期、型号、价格,那么委托和方法的声明都会变得很麻烦,而如果我们将热水器的引用传给警报器的方法,就可以在方法中直接访问热水器了。

现在我们改写之前的范例,让它符合 .Net Framework 的规范:

using System;

using System.Collections.Generic;

using System.Text;

namespace Delegate {

// 热水器

public class Heater {

private int temperature;

public string type = "RealFire 001"; // 添加型号作为演示

public string area = "China Xian"; // 添加产地作为演示

//声明委托

public delegate void BoiledEventHandler(Object sender, BoliedEventArgs e);

public event BoiledEventHandler Boiled; //声明事件

// 定义BoliedEventArgs类,传递给Observer所感兴趣的信息

public class BoliedEventArgs : EventArgs {

public readonly int temperature;

public BoliedEventArgs(int temperature) {

this.temperature = temperature;

}

}

// 可以供继承自 Heater 的类重写,以便继承类拒绝其他对象对它的监视 protected virtual void OnBolied(BoliedEventArgs e) {

if (Boiled != null) { // 如果有对象注册

Boiled(this, e); // 调用所有注册对象的方法

}

}

// 烧水。

public void BoilWater() {

for (int i = 0; i <= 100; i++) {

temperature = i;

if (temperature > 95) {

//建立BoliedEventArgs 对象。

BoliedEventArgs e = new BoliedEventArgs(temperature);

OnBolied(e); // 调用 OnBolied方法

}

}

}

}

// 警报器

public class Alarm {

public void MakeAlert(Object sender, Heater.BoliedEventArgs e) {

Heater heater = (Heater)sender; //这里是不是很熟悉呢?

//访问 sender 中的公共字段

Console.WriteLine("Alarm:{0} - {1}: ", heater.area, heater.type);

Console.WriteLine("Alarm: 嘀嘀嘀,水已经 {0} 度了:", e.temperature); Console.WriteLine();

}

}

// 显示器

public class Display {

public static void ShowMsg(Object sender, Heater.BoliedEventArgs e) { //静态方法

Heater heater = (Heater)sender;

Console.WriteLine("Display:{0} - {1}: ", heater.area, heater.type);

Console.WriteLine("Display:水快烧开了,当前温度:{0}度。", e.temperature);

Console.WriteLine();

}

}

class Program {

static void Main() {

Heater heater = new Heater();

Alarm alarm = new Alarm();

heater.Boiled += alarm.MakeAlert; //注册方法

heater.Boiled += (new Alarm()).MakeAlert; //给匿名对象注册方法

heater.Boiled += new Heater.BoiledEventHandler(alarm.MakeAlert); //也可以这么注册 heater.Boiled += Display.ShowMsg; //注册静态方法

heater.BoilWater(); //烧水,会自动调用注册过对象的方法

}

}

}

输出为:

Alarm:China Xian - RealFire 001:

Alarm: 嘀嘀嘀,水已经 96 度了:

Alarm:China Xian - RealFire 001:

Alarm: 嘀嘀嘀,水已经 96 度了:

Alarm:China Xian - RealFire 001:

Alarm: 嘀嘀嘀,水已经 96 度了:

Display:China Xian - RealFire 001:

Display:水快烧开了,当前温度:96度。

// 省略 ...

总结

在本文中我首先通过一个GreetingPeople的小程序向大家介绍了委托的概念、委托用来做什么,随后又引出了事件,接着对委托与事件所产生的中间代码做了粗略的讲述。

在第二个稍微复杂点的热水器的范例中,我向大家简要介绍了 Observer设计模式,并通过实现这个范例完成了该模式,随后讲述了.Net Framework中委托、事件的实现方式。

本文的源码可以在https://www.360docs.net/doc/9c4467024.html,/sourcecode/delegates-and-events.rar下载。

C#里的委托和事件实现Observer

C#里的委托和事件实现Observer 一、委托的简介 1、委托的声明: delegate HandlerName ([parameters]) 例如: public delegate void PrintHandler(string str); 委托声明定义了一种类型,它用一组特定的参数以及返回类型来封装方法。对于静态方法,委托对象封装要调用的方法。对于实例方法,委托对象同时封装一个实例和该实例上的一个方法。如果您有一个委托对象和一组适当的参数,则可以用这些参数调用该委托。 2、委托的使用: using System; public class MyClass { public static void Main() { PrintStr myPrinter = new PrintStr(); PrintHandler myHandler = null; myHandler += new PrintHandler(myPrinter.CallPrint); // 将委托链接到方法,来实例化委托 if(myHandler!=null) myHandler("Hello

World!"); // 调用委托,相当于匿名调用委托所链接的方法 Console.Read(); } } public delegate void PrintHandler(string str); // 声明委托类型 public class PrintStr { public void CallPrint(string input) { Console.WriteLine(input); } } 在C#中使用委托方法: ·创建委托所使用的方法必须和委托声明相一致(参数列表、返回值都一致) ·利用 +=、-=来进行委托的链接、取消链接或直接使用https://www.360docs.net/doc/9c4467024.html,bine和Delegate.Remove方法来实现 ·可以使用MulticastDelegate的实例方法GetInvocationList()来获取委托链中所有的委托

C#委托及事件

C#委托及事件 在C#中,委托(delegate)是一种引用类型,在其他语言中,与委托最接近的是函数指针,但委托不仅存储对方法入口点的引用,还存储对用于调用方法的对象实例的引用。 简单的讲委托(delegate)是一种类型安全的函数指针,首先,看下面的示例程序,在C++中使用函数指针。 首先,存在两个方法:分别用于求两个数的最大值和最小值。 int Max(int x,int y) { return x>yx:y; } int Min(int x,int y) { return x } 上面两个函数的特点是:函数的返回值类型及参数列表都一样。那么,我们可以使用函数指针来指代这两个函数,并且可以将具体的指代过程交给用户,这样,可以减少用户判断的次数。 下面我们可以建立一个函数指针,将指向任意一个方法,代码如下所示: 建立一个委托类型,并声明该委托可以指向的方法的签名(函数原型)delegate void MyDelegate(int a,int b); 2.建立一个委托类的实例,并指向要调用的方法 用委托类实例调用所指向的方法 int c=md(4,5); 下面通过实例来演示C#中委托的使用。

案例操作020601:利用委托实现方法的 动态调用 首先,添加如下控件: 两个RadioButton,分别用来让用户选 择求最大值以及求最小值 二个TextBox,用来输入两个操作数 一个TextBox,用来显示运算结果 一个Button,用来执行运算 界面如下图所示: 下一步,在窗口中添加两个方法:Max,Min,这两方法的代码如下: int Max(int x,int y) { return x>yx:y; } int Min(int x,int y) { return x } 窗口中的代码,如下图所示:

C#委托事件详解

C# 中的委托和事件 引言 委托和事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉得心里别(biè)得慌,混身不自在。本文中,我将通过两个范例由浅入深地讲述什么是委托、为什么要使用委托、事件的由来、.Net Framework中的委托和事件、委托和事件对Observer设计模式的意义,对它们的中间代码也做了讨论。 将方法作为方法的参数 我们先不管这个标题如何的绕口,也不管委托究竟是个什么东西,来看下面这两个最简单的方法,它们不过是在屏幕上输出一句问候的话语: public void GreetPeople(string name) { // 做某些额外的事情,比如初始化之类,此处略 EnglishGreeting(name); } public void EnglishGreeting(string name) { Console.WriteLine("Morning, " + name); } 暂且不管这两个方法有没有什么实际意义。GreetPeople用于向某人问好,当我们传递代表某人姓名的name参数,比如说“Jimmy”,进去的时候,在这个方法中,将调用EnglishGreeting方法,再次传递name参数,EnglishGreeting则用于向屏幕输出“Morning, Jimmy”。

现在假设这个程序需要进行全球化,哎呀,不好了,我是中国人,我不明白“Morning”是什么意思,怎么办呢?好吧,我们再加个中文版的问候方法: public void ChineseGreeting(string name){ Console.WriteLine("早上好, " + name); } 这时候,GreetPeople也需要改一改了,不然如何判断到底用哪个版本的Greeting问候方法合适呢?在进行这个之前,我们最好再定义一个枚举作为判断的依据: public enum Language{ English, Chinese } public void GreetPeople(string name, Language lang){ //做某些额外的事情,比如初始化之类,此处略 swith(lang){ case Language.English: EnglishGreeting(name); break; case Language.Chinese: ChineseGreeting(name); break; } } OK,尽管这样解决了问题,但我不说大家也很容易想到,这个解决方案的可扩展性很差,如果日后我们需要再添加韩文版、日文版,就不得不反复修改枚举和GreetPeople()方法,以适应新的需求。 在考虑新的解决方案之前,我们先看看 GreetPeople的方法签名:

唯一看明白额委托与事件讲解

一、在控制台下使用委托和事件 我们都知道,C#中有“接口”这个概念,所谓的“接口”就是定义一套标准,然后由实现类来具体实现其中的方法,所以说“接口,是一组类的抽象”。同样道理,我们可以将“委托”理解为“方法的抽象”,也就是说定义一个方法的模板,至于这个方法具体是怎么样的,就由方法自己去实现。 我们知道接口的最大好处就是可以实现多态,同理,“委托”是可以实现方法的多态,当我们想调用某个具体方法的时候,我们不直接调用这个方法,而是去调用这个委托。当然,我们必须在具体方法和委托之间建立某种关联。 下面我们来看例子。 首先,我们定义一个委托: public delegate void SaySomething(string name); 这跟抽象方法的语法格式很相似,只是多了一个关键字delegate。既然是对方法的一种抽象,那么我们最关注的当然就是方法的返回值以及方法的参数了。所以上面红色的部分就是我们定义出来的一个规矩,如果某个方法想委托我去做事,那么请你遵循我的规矩,就是返回值为void,参数为一个字符串。我们这个委托的含义是,当某个人来了,就向他说点东西。 好,既然我们已经定义了这个规矩,下面我们就定义具体的方法了。 public void SayHello(string name) { Console.WriteLine("Hello," + name + "!"); } public void SayNiceToMeetYou(string name) { Console.WriteLine("Nice to meet you," + name + "!"); } 我们这里一共定义了两个方法,一个是向某人说Hello,另一个是向某人说Nice to meet you。我们看到,这里定义的两个方法的返回值和参数跟我们前面定义的“委托”是一致的。 接下来,我们来看事件。 public event SaySomething come;

委托事件1

委托 定义委托的语法和定义方法比较相似,只是比方法多了一个关键字delegate,我们都知道方法就是将类型参数化,所谓的类型参数化就是说该方法接受一个参数,而该参数是某种类型的参数,比如int、string等等;而委托是将方法参数化,说了上面的那个类型参数化之后,相信你也能猜到方法参数化的意思了,对,就是将方法作为一个参数传到一个委托中。 首先来看看声明委托的语句: public deletate void MyDelegate(); public:访问修饰符delegate:关键字void:返回类型MyDelegate:委托名称( ):参数列表 看到声明大家会想了,为什么该委托的返回值,参数列表要这样的,我不能返回一个string,一个int么?我不能给委托加几个参数么?答案是:当然可以,但委托的定义是相对于方法来说的,因为得你的委托最终是要来注册方法的,而你的方法是具有某种签名的,所以你要给怎样签名的方法来声明一个委托,该委托就要和该方法具有同等的签名,就类似于你用一个int 类型的变量去接受一个string类型的值,显然是不行的(个人理解).... * 委托只要定义就可以了,我们并不需要关心他的实现 委托的使用 注册委托有两种方法: 第一种:直接将方法赋值[=]或者用“+=”给一个委托==>委托名=[+=] 方法名 第二种:委托本质也是一个类,只是一个特殊的类,所以我们也可以实例化一个委托对象通过委托构造函数来注册委托==》委托名对象名= new 委托名(方法名)

了解了委托的声明和使用,我们就可以来看小例子来加深理解了 首先看看界面: 界面上就是简单的四个按钮两个属于委托,两个属于事件,都是一个用来执行,一个用来干扰,以便于来理解委托事件 然后看后台代码,首先我定义了一个Test类,声明委托,实例了委托,还声明了事件,写了个方法用来触发事件,代码如下: 1public class Test 2 { 3//声明一个委托 4public delegate void MyDelegate(); 5 6//创建一个委托实例 7public MyDelegate myDel; 8//声明一个事件 9public event MyDelegate EventMyDel; 10 11//事件触发机制(必须和事件在同一个类中) 外界无法直接用EventMyDel()来触发事件 12public void DoEventMyDel() 13 { 14 EventMyDel(); 15 } 16 }

委托及事件响应button_click

C#事件及响应方法——让你明白private void button1_Click(object se nder, System.EventArgs e) C#语言自C/C++演变而来。它是现代、简单、完全面向对象和类型安全的。C#语言是微软公司针对.Net平台才推出来的一门新语言,作为.Net平台的第一语言,它几乎集中了所有关于软件开发和软件工程研究的最新成果。面向对象、类型安全、组件技术、自动内存管理、跨平台异常处理、版本控制、代码安全管理…… 在.NET应用程序开发中,不管是WEB Forms(https://www.360docs.net/doc/9c4467024.html,)还是Windows Forms,都涉及到大量对象的事件响应及处理,比如客户在线提交一份订单、或是在Windows窗口上移动鼠标等都将有事件发生。那么在C#中,是怎样声明事件并为事件添加响应方法的呢?下面的文章对此为大家作了详细的讲述。 原理简介 在C#中,一个类可以有域(Fields)、属性(Properties)、方法(Methods)、索引(Ind exs)、事件(Events)等成员,其中事件(Events)成员就是用来声明一个类事件的。在类中声明一个事件成员一般采用如下的语法形式: public event 代表名事件名。 如在Control类中声明了一个Click事件成员,其语法如下: public event EventHandler Click; 在C#中,增加了一个新的数据类型delegate(代表)来解决事件处理问题。代表数 据类型非常类似于C语言中的指针,其与指针不同的是,其是代码是安全的,可管理的。 由于C#本身的简易性,对于没有使用过C及指针的程序来说,理解delegate也是非常容易的。 在C#中,通过使用delegate,你可以通过“+=”(加等于)操作符非常容易地为.Ne t对象中的一个事件添加一个甚至多个响应方法;还可以通过非常简单的“-=”(减等于)操作符取消这些响应方法。如下面为temp按钮添加Click事件的语句: temp.Click+=new System.EventHandler(this.Test);//为test添加事件处理方 法 在上面声明事件的语句中,Eventhandler是一个delegate(代表)类型,其在.Net类库中如下声明的: public delegate void EventHandler(object sender,EventArgs e); 这样,所有形如:void 函娄名(object 参数名,EventArgs参数名);的函数都可以作为Co ntrol类的Click事件响应方法了。如下面所定义的一个事件响应方法: private void button1_Click(object sender, System.EventArgs e)

C#委托与事件以及应用

这些很重要在MVC EF4.1 里都会有很多拉姆达表达式的影子在做组件开发用户控件开发事件的运用一定也是少不了的稍微深入点儿的说用于两个对象之间的通讯用来解耦用委托事件是很不错的选择而设计模式里的观察者模式也是基于委托事件的应用还有做winform 开发的里的线程WPF SL 的异步调用等都是有这委托的影子的所以这个还是很重要的。也是一定要掌握的~ 希望通过这篇文章能让大家更加了解委托以及如何运用~ 一.委托以及延伸 先看下MSDN 的介绍吧~------ delegate 通俗的说委托就是可以实现把方法做为变量来传递 1.先写个最简单的委托的用法 静态的和非静态方法的 结果会输出 您好wlf

Hello wlf 这是最原始的委托 2. 进化为匿名方法 声明完委托后还要声明方法是不是很麻烦如果不声明方法用匿名方法可以帮我们看 代码减少了很多吧~ 3.再进化为拉姆达表达式 上面的虽然简单了不少但是还能更简单的用拉姆达表达式~ 看这就是拉姆达表达式的演变一定要会这个~ 因为在EF LINQ 等有很多用拉姆达表达式的~ 4.用Action 和Func继续简化 上面的代码以及很简单了但是还有个很不爽的一点~ 要声明委托!可以不声明么?当然可以~ 先看MSDN介绍Action和Func 通俗的讲这两个都是用来帮你声明委托 Action 用于帮你声明没用返回值的委托Func则是有返回值的最后一个参数为返回值

看~ 以前的版本总是要声明一个HelloWorld 的委托现在只有两行代码就可以了下面顺便掩饰了有返回值的func 的例子。第一个参数是传递 参数的类型第二个是返回值的类型~ 这里说下这俩函数都有16个重载~ 所以多个参数是没问题的~ 5.说说委托的好处 委托的好处应用文章开始已经说了很多了这里就拿文章开头的例子体会下委托的好处 当我们再多一种语言来问好时只需增加一种Action 即可而不需要改动sayHello 方法否则这里将充满了if else 的判断 6.工作里的应用 再LINQ 或EF 里都有个很重要的数据刷选的功能WHERE 它的参数就是Func 直接上代码自己看注释~

unity3D学习之委托、事件全解析(二)

废话就不多说了,直接进入今天的主题-事件在我们所接触到的事件一般分两种:一种是自定义的,自定义的事件需要自己对其进行赋值。 一种是控件提供方定义的,如:ngui,控件事件只需要查找控件定义的事件列表,选择所需要的进行操作即可。 当然,我们的话题是上面第一种啦。 实例模拟场景为:文章来自【狗刨学习网】 游戏战斗中,猪脚在指定的一片区域中,存在4只怪物,他的目的就是一只一只找到并消灭该区域的怪物。 简易流程:查询目标->行走->攻击,依次循环 ok,在此,我用代码快速模拟这样一个情景,建立一个Hero类,如下: using UnityEngine; using System.Collections; using System.Collections.Generic; using System;

// 英雄角色 public class Hero : MonoBehaviour { //当前目标id public int TargetID=0; public List ListMonster; void Start() { InvokeRepeating("selectTarget", 0.3f, 0.3f); } // 查询目标 private void selectTarget() { if (TargetID==0) { if (ListMonster.Count > 0) { for (int i = 0; i <= ListMonster.Count; i++) { TargetID = ListMonster[i];[/i] WalkToTarget(TargetID);

你可能不知道的陷阱:C#委托和事件的困惑

你可能不知道的陷阱:C#委托和事件的困惑 . 问题引入 通常,一个C 语言学习者登堂入室的标志就是学会使用了指针,而成为高手的标志又是“玩转指针”。指针是如此奇妙,通过一个地址,可以指向一个数,结构体,对象,甚至函数。最后的一种函数,我们称之为“函数指针”(和“指针函数”可不一样!)就像如下的代码: 1 2 3 int func(int x); /* 声明一个函数 */ int (*f) (int x); /* 声明一个函数指针 */ f=func; /* 将func 函数的首地址赋给指针f */ C 语言因为函数指针获得了极强的动态性,因为你可以通过给函数指针赋值并动态改变其行为,我曾在单片机上写的一个小系统中,任务调度机制玩的就是函数指针。 在.NET 时代,函数指针有了更安全更优雅的包装,就是委托。而事件,则是为了限制委托灵活性引入的新“委托”(之所以为什么限制,后面会谈到)。同样,熟练掌握委托和事件,也是C#登堂入室的标志。有了事件,大大简化了编程,类库变得前所未有的开放,消息传递变得更加简单,任何熟悉事件的人一定都深有体会。 但你也知道,指针强大,高性能,带来的就是危险,你不知道这个指针是否安全,出了问题,非常难于调试。事件和委托这么好,可是当你写了很多代码,完成大型系统时,心里是不是总觉得怪怪的?有当年使用指针时类似的感觉? 如果是的话,请看如下的问题: 1. 若多次添加同一个事件处理函数时,触发时处理函数是否也会多次触发? 2. 若添加了一个事件处理函数,却执行了两次或多次”取消事件“,是否会报错? 3. 如何认定两个事件处理函数是一样的? 如果是匿名函数呢? 4. 如果不手动删除事件函数,系统会帮我们回收吗? 5. 在多线程环境下,挂接事件时和对象创建所在的线程不同,那事件处理函数中的代码将在哪个线程中执行? 6. 当代码的层次复杂时,开放委托和事件是不是会带来更大的麻烦? 列下这些问题,下面就让我们讨论这些”尖酸刻薄“的问题。 二. 事件订阅和取消问题 我们考虑一个典型的例子:加热器,加热器内部加热,在达到温度后通知外界”加热已经完成“。 尝试写下如下测试类:

js事件委托

JavaScript中的事件委托 传统的事件处理 事件委托就是在一个页面上使用一个事件来管理多种类型的事件。这并不是一个新的想法,但对于把握性能来说却很重要。通常情况,你会在web应用程序中看到这样的代码:document.getElementById("help-btn").onclick = function(event){ openHelp(); }; document.getElementById("save-btn").onclick = function(event){ saveDocumen t(); }; document.getElementById("undo-btn").onclick = function(event){ undoChanges (); }; 这种传统的编码方式给每个元素分配单独的事件处理方法。对于交互少的站点来说,这样做是可以的。然而,对于大型的wen应用程序,当存在大量的事件处理的时候,就会显得反应迟钝。这里要关注的不是速度问题,而是内存占用问题。如果有数百个交互,DOM元素和JavaScript 代码就会有数百个关联。 web应用需要占用的内存越多,它的响应速度就越慢。事件委托能将这个问题减小。 事件冒泡及捕获 要不是事件的下面这些属性,事件委托将成为可能。早期的web开发,浏览器厂商很难回答一个哲学上的问题:当你在页面上的一个区域点击时,你真正感兴趣的是哪个元素。这个问题带来了交互的定义。在一个元素的界限内点击,显得有点含糊。毕竟,在一个元素上的点击同时也发生在另一个元素的界限内。例如单击一个按钮。你实际上点击了按钮区域、body元素的区域以及html元素的区域。

C#委托与事件

我们抛弃各种C#参考书中桀骜难懂的事件与委托概念,设想一个情景来理解事件与委托的使用:有一家IT公司,董事长不希望自己的雇员在上班时间玩游戏,但又不可能每时每刻都盯着每个雇员,因此,他希望使用一种新的方式实现监视雇员的效果:如果有雇员违反规定,某个设备或专门的监查人员将自动发出一个消息通知他,董事长只需要在事情发生时进行处理。 因此,这个用例实际上是两种类型——董事长类与雇员类——之间的交互,下面的代码将给读者展示如何使用委托与事件机制实现这种交互: 首先,我们需要在董事长类与雇员类之间定义一个委托类型,用于传递两者之间的事件,这个类型就是一个监视设备或专门负责打小报告的监查人员: public delegate void DelegateClassHandle(); 定义一个委托的过程类似方法的定义,但它没有方法体。定义委托一定要添加关键字delegate。由于定义委托实际上相当一个类,因此可以在定义类的任何地方定义委托。另外,根据委托的可见性,也可以添加一般的访问修饰符,如public、private和protected。 委托的返回值类型为void,这并非表示委托类型本身带有返回值,该返回值类型是指委托的目标函数类型,即它委托的一个事件处理函数返回值是void类型。 新建一个雇员类Employee,其代码如下: public class Employee { public event DelegateClassHandle PlayGame; public void Games() { if (PlayGame != null) { PlayGame(); } } } 雇员类Employee代码中定义了一个DelegateClassHandle类型的事件PlayGame,它的定义方式也很特殊,首先必须使用关键字event,表示PlayGame是一个事件,同时还必须声明该事件的委托类型为DelegateClassHandle,即将来由该类型的委托对象负责通知事件。 如果有雇员开始玩游戏,它将执行Games方法,而只要该方法一被调用,就会触发一个事件PlayGame,然后董事长就会收到这个事件的消息——有人在玩游戏了。 董事长类代码如下,他有一个方法Notify用于接收消息: public class Admin { public void Notify() {

C#委托、事件、自定义事件的理解

C#委托、事件、自定义事件的理解 一、委托 委托类似于函数指针,但函数指针只能引用静态方法,而委托既能引用静态方法,也能引用实例方法。委托使用分三步:1、委托声明。2、委托实例化。3、委托调用。 例程一: using System; namespace 委托 { delegate int NumOpe(int a,int b); //委托声明 class Class1 { static void Main(string[] args) { Class1 c1 = new Class1(); NumOpe p1 = new NumOpe(c1.Add); //委托实例化 Console.WriteLine(p1(1,2)); //委托调用 Console.ReadLine(); } private int Add(int num1,int num2) { return(num1+num2); } } } 例中,委托NumOpe引用了方法Add。 委托声明了以后,就可以象类一样进行实例化,实例化时把要引用的方法(如:Add)做为参数,这样委托和方法就关联了起来,就可以用委托来引用方法了。 委托和所引用的方法必须保持一致: 1)参数个数、类型、顺序必须完全一致。2)返回值必须一致。 二、事件 事件有很多,比如说鼠标的事件:MouserMove,MouserDown等,键盘的事件:KeyUp,KeyDown,KeyPress。 有事件,就会有对事件进行处理的方法,而事件和处理方法之间是怎么联系起来的呢?委托就是他们中间的桥梁,事件发生时,委托会知道,然后将事件传递给处理方法,处理方法进行相应处理。

委托和事件的区别

委托和事件的区别(讲的很详细)--转 委托和事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉得心里别(biè)得慌,混身不自在。本文中,我将通过两个范例由浅入深地讲述什么是委托、为什么要使用委托、事件的由来、.Net Framework中的委托和事件、委托和事件对Observer设计模式的意义,对它们的中间代码也做了讨论。 将方法作为方法的参数 我们先不管这个标题如何的绕口,也不管委托究竟是个什么东西,来看下面这两个最简单的方法,它们不过是在屏幕上输出一句问候的话语: public void GreetPeople(string name) { // 做某些额外的事情,比如初始化之类,此处略 EnglishGreeting(name); } public void EnglishGreeting(string name) { Console.WriteLine("Morning, " + name); } 暂且不管这两个方法有没有什么实际意义。GreetPeople用于向某人问好,当我们传递代表某人姓名的name参数,比如说“Jimmy”,进去的时候,在这个方法中,将调用EnglishGreeting方法,再次传递name参数,EnglishGreeting则用于向屏幕输出“Morning, Jimmy”。 现在假设这个程序需要进行全球化,哎呀,不好了,我是中国人,我不明白“Morning”是什么意思,怎么办呢?好吧,我们再加个中文版的问候方法: public void ChineseGreeting(string name){ Console.WriteLine("早上好, " + name); } 这时候,GreetPeople也需要改一改了,不然如何判断到底用哪个版本的Greeting问候方法合适呢?在进行这个之前,我们最好再定义一个枚举作为判断的依据: public enum Language{ English, Chinese } public void GreetPeople(string name, Language lang){ //做某些额外的事情,比如初始化之类,此处略 swith(lang){ case Language.English:

委托与事件代码详解与(Object_sender,EventArgs_e)详解

1 委托与事件代码详解与(Object sender,EventArgs e )详解 using System; using System.Collections.Generic; using System.Text; namespace @Delegate //自定义命名空间,新建控制台程序,命名后自动添加 { // 热水器 public class Heater { private int temperature; public string type = "RealFire 001"; // 添加型号作为演示 public string area = "China Xian"; // 添加产地作为演示 //声明委托 public delegate void BoiledEventHandler(Object sender, BoiledEventArgs e);/*BoiledEventHandler 相当于一个类型(属于委托),与String 地位等同,它所声明的参数形式与后来它要包含的方法的参数形式必须是一致的,例如黄色加亮部分的方法*/ public event BoiledEventHandler Boiled; /*声明事件。相当于封装BoiledEventHandler 类型的对象(变量) Boiled ,使之在类的内部总是pravite 的,而使+=和-=的访问权限为声明时的修饰符权限*/ // 定义BoiledEventArgs 类,传递给Observer 所感兴趣的信息。//Observer 设计模式 public class BoiledEventArgs : EventArgs { public readonly int temperature; public BoiledEventArgs(int temperature) { this.temperature = temperature; } } // 可以供继承自 Heater 的类重写,以便继承类拒绝其他对象对它的监视//虚方法可以在类中覆盖重写 protected virtual void OnBoiled(BoiledEventArgs e) { if (Boiled != null) { // 如果有对象注册 Boiled(this, e); // 调用所有注册对象的方法 } } // 烧水。 public void BoilWater() { for (int i = 0; i <= 100; i++) {

JavaScript事件冒泡,事件捕获,事件处理,事件委托

本文由我司收集整编,推荐下载,如有疑问,请与我司联系JavaScript 事件冒泡,事件捕获,事件处理,事件委托2017/03/16 1429 早期的事件,是作为分担服务器运算负载的一种手段,实文 档或者浏览器窗口中发生的一些特定的交互瞬间,如点击按钮,拖放文件等。我们 可以使用侦听器来预定事件,当事件发布时候就可作出相应的响应,这种模式称为 观察者模型。 事件流是从页面接收事件的顺序。在一个html 页面中,dom 元素组成一颗dom 树,由于子元素一般所处的位置都会在父元素之中。那么,当这个子元素被点击时 候,可以认为子元素受到了点击,也可以认为父元素受到点击。那么确定谁先接收 这个点击事件就成了浏览器需要解决的问题。IE 在处理上述事件时候,是由事件 开始最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到 根节点。这就是事件冒泡。!DOCTYPE html html lang=“en”head meta charset=“UTF-8”title test /title /head body div id=“container” /div /body /html 在如上代码里面,如果单击了div 元素,那么接着body 、html 、document 会按 着顺序收到点击事件。Netscape Communicator 团队提出了一种与IE 完全相反的一种解决办法,接收事件的顺序为根节点到具体的节点,这种方法就是事件捕获。也 就是在上面的这个例子中,接收点击事件的顺序为document、html 、body 、div 。“DOM2级事件”规定的事件流包括三个阶段:事件捕获阶段、处于目标阶段 和事件冒泡阶段。首先发生的是事件捕获,然后处于目标阶段,最后才事件冒泡。 依然以上面的代码为例子,单击div 时,首先document 获得点击事件,依次到 html 、body ,之后事件捕获结束。事件捕获的意义在于,能够在目标获得点击事 件之前截获事件,并对其作出相应的处理。处于目标阶段时,div 能够执行绑定的事 件处理程序,之后再到事件冒泡阶段。纵然“DOM2级事件”规范明确规定捕获阶段 不会涉及事件目标,但是浏览器基本都会实现在捕获阶段触发事件对象上的事件, 也就是说,可以在事件捕获阶段触发事件对象的事件处理程序,这种情况通常都需 要在绑定事件处理程序的情况下指明可在捕获阶段触发。如果非必须,最好不要使

从一则案例看行政委托与行政授权的区别

(https://www.360docs.net/doc/9c4467024.html,) 从一则案例看行政委托与行政授权的区别 一、一则案例的引出 2013年4月26日,原告饶某以特快专递的方式向被告临川区温泉镇人民政府邮寄了政府信息公开的申请,其申请事项为:请求被告公开2010年、 2011年、2012年温泉镇人民政府每年征收的社会抚养费总数及用途。被告于2013年4月28日收到了原告的政府信息公开申请书,但是超过法定期限未予答复。原告遂依据相关的法律规定向法院提起行政诉讼。 被告辩称:原告饶某诉请公布社会抚养费的征收情况与其没有法律上的利害关系,其不具有提起行政诉讼的主体资格。社会抚养费的征收机关是当地计划生育行政部门,被告温泉镇人民政府是受委托的机关,不存在直接向原告饶某公开社会抚养费的征收情况,原告诉请的对象错误,被告主体不适格。 被告是否具有向原告履行政府信息公开的法定职责,取决于被告的诉讼主体是否适格。行政委托不同于行政授权,被告如果是受委托行政机关,就不具有诉讼主体资格。本案存在的主要问题是被告主体是否适格的问题。行政委托案件中,被告应是行使委托的行政机关。 二、行政委托与行政授权的区别 (一)行政委托的概念与构成要件 行政委托是指行政机关将其管辖权的一部分交由行政机关之外的组织或者个人,并以委托机关的名义行使行政职能。从广义上讲,行政机关之间的委托也应当包括在内。 行政委托的法律要件包括以下四个方面的内容: 1、委托必须有法定依据。行政机关必须在法律、法规及规章规定可以委托时,才能委托。没有法定依据的委托,叫做…自行委托?,是不合法的,也是无效的。 2、委托行政机关必须拥有法定权限。委托机关在进行行政委托时,其委托给受委托人的公权力必须是其自身合法拥有的职权。如果行政机关把一项本身不拥有的公权力委托给受委托人行使,这显然是滥用职权,超越权限的委托当然无效。

委托与事件的区别

委托与事件的区别 委托和事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C# 时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没 有过去的人每次见到委托和事件就觉得心里别(biè)得慌,混身不自在。本文中,我将通过两个 范例由浅入深地讲述什么是委托、为什么要使用委托、事件的由来、.Net Framework中的委托和 事件、委托和事件对Observer设计模式的意义,对它们的中间代码也做了讨论。 将方法作为方法的参数 我们先不管这个标题如何的绕口,也不管委托究竟是个什么东西,来看下面这两个最简单的方法,它们不过是在屏幕上输出一句问候的话语: public void GreetPeople(string name) { // 做某些额外的事情,比如初始化之类,此处略 EnglishGreeting(name); } public void EnglishGreeting(string name) { Console.WriteLine("Morning, " + name); } 暂且不管这两个方法有没有什么实际意义。GreetPeople用于向某人问好,当我们传递代表某人姓 名的name参数,比如说“Jimmy”,进去的时候,在这个方法中,将调用EnglishGreeting方法,再 次传递name参数,EnglishGreeting则用于向屏幕输出“Morning, Jimmy”。 现在假设这个程序需要进行全球化,哎呀,不好了,我是中国人,我不明白“Morning”是什么意思,怎么办呢?好吧,我们再加个中文版的问候方法: public void ChineseGreeting(string name){ Console.WriteLine("早上好, " + name); } 这时候,GreetPeople也需要改一改了,不然如何判断到底用哪个版本的Greeting问候方法合适呢?在进行这个之前,我们最好再定义一个枚举作为判断的依据: public enum Language{ English, Chinese }

详解C#委托,事件与回调函数

详解C#委托,事件与回调函数 .Net编程中最经常用的元素,事件必然是其中之一。无论在https://www.360docs.net/doc/9c4467024.html,还是WINFrom开发中,窗体加载(Load),绘制(Paint),初始化(Init)等等。 “protected void Page_Load(object sender, EventArgs e)”这段代码相信没有人不熟悉的。细心一点一定会发现,非常多的事件方法都是带了“object sender, EventArgs e”这两个参数。这是不是和委托非常相似呢? 一、委托(有些书中也称为委派) 委托是什么呢?这个名字的意思已经赋予了我们想象的空间,你是编程的,你现在正在写一个https://www.360docs.net/doc/9c4467024.html,网页,而JS是你不熟悉的,于是你委托你的一位同事来帮助你完成JS部分。这就是委托,把你所不能做的事情交给其他人去做。而怎么知道是哪个人去做呢?当然是要知道名字!而为了区别名字一样的不同人,因此,需要描述一个特征。 在C#中,委托的作用是这样描述的:委托就像一个函数的指针,在程序运行时可以使用它们来调用不同的函数。这个其实和你委托同事完成 JS代码一样。如果有两位同事可以做这件事情,他们只要做的结果能够满足你的需求(就像一个接口),尽管他们做的过程不一样,并且作出的效果也不一样,但是,能够达到你的要求就可以了。 1、简单的委托 那委托需要承载哪些信息呢?首先,它存储了方法名,还有参数列表(方法签名),以及返回的类型。比如: delegate string/*返回类型*/ ProcessDelegate(int i); 这就是一个委托的定义。蓝色部分是声明委托的关键字,红色部分是返回的类型,而黑色部分是委托的类型名,和一个类名差不多,而()里的就是参数部分。它的意思是,你要使用这个委托来做事情的话,那么,做事情的方法必须满足以下条件:

菜鸟入门 认识C#中的委托和事件

引言 委托和事件在.Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉得心里别(biè)得慌,混身不自在。本文中,我将通过两个范例由浅入深地讲述什么是委托、为什么要使用委托、事件的由来、.Net Framework中的委托和事件、委托和事件对Observer设计模式的意义,对它们的中间代码也做了讨论。 将方法作为方法的参数 我们先不管这个标题如何的绕口,也不管委托究竟是个什么东西,来看下面这两个最简单的方法,它们不过是在屏幕上输出一句问候的话语: public void GreetPeople(string name) { // 做某些额外的事情,比如初始化之类,此处略 EnglishGreeting(name); } public void EnglishGreeting(string name) { Console.WriteLine("Morning, " + name); } 暂且不管这两个方法有没有什么实际意义。GreetPeople用于向某人问好,当我们传递代表某人姓名的name参数,比如说“Jimmy”,进去的时候,在这个方法中,将调用EnglishGreeting方法,再次传递name参数,EnglishGreeting则用于向屏幕输出“Morning, Jimmy”。 现在假设这个程序需要进行全球化,哎呀,不好了,我是中国人,我不明白“Morning”是什么意思,怎么办呢?好吧,我们再加个中文版的问候方法: public void ChineseGreeting(string name){ Console.WriteLine("早上好, " + name); } 这时候,GreetPeople也需要改一改了,不然如何判断到底用哪个版本的Greeting问候方法合适呢?在进行这个之前,我们最好再定义一个枚举作为判断的依据: public enum Language{ English, Chinese } public void GreetPeople(string name, Language lang){ //做某些额外的事情,比如初始化之类,此处略 swith(lang){ case Language.English:

C#中的委托(Delegate)和事件(Event)-例子解释

https://www.360docs.net/doc/9c4467024.html,/uid-576762-id-2733751.html C#中的委托(Delegate)和事件(Event)2007-11-30 11:40:00 分类: 把C#中的委托(Delegate)和事件(Event)放到现在讲是有目的的:给下次写的设计模式——观察者(Observer)有一个参考。 委托和事件应该是C#相较于C++等之前的非托管的语言提出的一个新的术语(term)。“旧瓶装新酒”这样的描述似乎有些“贬义”,但确实是这样。委托也好,事件也好最初的起源是C/C++中的函数指针,关于函数指针的简单介绍可以参见我以前的一篇《C/C++中指向函数的指针》。不过旧瓶装新酒没有什么不好,反而给人添加了许多新滋味。 1. Function pointer--the origin of delegates and events . 书回正传,既然函数指针是它们(委托和事件)的起源。那我们先看看什么情况下我们需要函数指针。函数指针最常用的方式就是回调(callback)——在函数休内回调主函数里的函数。有些绕口,看代码: //Platform: WinXP + VC6.0 #include #include using namespace std; void max(int a,int b) { cout<<"now call max("<b?a:b; cout<

相关文档
最新文档