分享
 
 
 

[原创]用C++模拟C#的event机制

王朝c#·作者佚名  2006-01-31
窄屏简体版  字體: |||超大  

本文章为farproc.com站长袁晓辉(海洋)的原创文章,作者保留所有权利。如果转载,请务必注明来自www.farproc.com!!

作为一个C++程序员,学习C#应该不是一件很困难的事情,因为C#的许多特性都是从C++“继承”(不精确的说法)来的。但是C#作为一门全新的编程语言,它必然有自己的新特性,而这些C++中并不存在的新特性正是我们从C++转向C#的过程中必须认真重新学习的东西。“事件(event)”是C#的一个比较简单的新特性,我们今天就从“事件”开始,看一看C#的事件到底是怎么回事。

C#的事件和Windows窗口编程中提到的“事件”、“消息”、“事件驱动”等在概念上是很类似的。我们在一个窗口上移动鼠标指针,系统就会产生WM_MOUSEMOVE消息(在vb中就是激发mousemove事件),只要我们告诉系统一个函数指针,系统就会通过这个回调函数通知我们,这是Windows窗口编程中的“事件”。C#中的事件的外延更广,任何一个对象都可以拥有事件,客户可以“定制”该对象的事件,当该对象的内部状态发生特定的改变时,就会通过定制事件时指定的函数代理(delegate)调用这个函数通知客户。当客户不在需要事件通知时,可以“撤消”对该事件的定制。

Now is the show time!模仿秀现在开始!

一、C#版

我们先来看一个C#中一个“事件”的最简单例子:

在VS.NET中新建一个C#的Console

Application,项目名称为“eventtest”。为该项目新加一个类MyClass,对应的源文件为MyClass.cs代码如下:

using System;

namespace eventtest

{

//定义EventHandler函数代理

public delegate void EventHandler();

/// <summary>

///

/// </summary>

public class MyClass

{

//构造函数

public MyClass(){}

//声明一个事件

public event

EventHandler AEvent;

//激发事件

public void

FireEvent()

{

if(AEvent != null)

{

//直接把event当做函数调用

AEvent();

}

}

}

}

public event EventHandler

AEvent;就是给MyClass定义了一个事件(通过event关键字),其事件处理函数(通知函数)的原型由EventHandler函数代理(类似C++中的函数指针)指定。

FireEvent()成员函数用于激发该事件,如果客户定制了该event,当本类对象的FireEvent()公开方法被调用时,客户应该可以得到通知。在Main函数里写如下代码:

using System;

namespace eventtest

{

/// <summary>

/// Class1 的摘要说明。

/// </summary>

class Class1

{

/// <summary>

/// 应用程序的主入口点。

/// </summary>

[STAThread]

static void Main(string[] args)

{

MyClass

Obj = new MyClass();

Obj.AEvent

+= new EventHandler(MyEventHandler);//定制事件

Obj.FireEvent();//这行将导致MyEventHandler被调用

Obj.AEvent

-= new EventHandler(MyEventHandler);//撤消事件

Obj.FireEvent();//这里将不会引发事件

Console.WriteLine("结束!");

Console.ReadLine();

}

//事件处理函数

public static void

MyEventHandler()

{

Console.WriteLine("This

is a event!");

}

}

}

首先写一个具有适当形式的事件处理(通知)函数

MyEventHandler,然后通过Obj.AEvent

+= new EventHandler(MyEventHandler)来定制事件。通过“-=”来撤消事件定制。

运行程序我们可以发现,当客户(Class1)定制了Obj的AEvent事件后,在Obj的FireEvent()成员函数被调用时,客户可以在MyEventHandler函数中得到通知(在这里只是简单地输出一个文本)。而当客户撤消该事件的定制后,就不会再得到该事件通知。

二、C++版

下面我们在C++中模拟该机制:

由于C++不支持event关键字,我们就必须自己写代码。在这里我通过模板类的手段来实现,因为该手段实现的效果和C#比较类似。

在VC6中新建一个win32

console app,命名为“cppevent“。新建一个.h头文件,命名为“event.h”,代码如下:

//event.h

template <typename Handler>

class event

{

private:

Handler m_Handler;

protected:

//模拟C# event 的add/remove访问器

//如果要重新实现add/remove请在派生类中重写这两个函数

virtual void add(const Handler value){m_Handler = value;};

virtual void remove(const Handler value){if(value == m_Handler)m_Handler = NULL;};

public:

//构造函数

event():m_Handler(NULL){}

//+= 操作符

event& operator += (const Handler value)

{

add(value);

return *this;

}

//-=操作符

event& operator -= (const Handler value)

{

remove(value);

return *this;

}

//PFN_EVENT_HANDLE 操作符

operator Handler()

{

return m_Handler;

}

};

为了能够在定义是指定事件处理函数的原型,我使用了template,为了能和C#一样用+=和-=来定制和撤消事件,我重载了这两个操作符(C#不支持操作符重载),为了能像C#一样直接把event当做函数调用,我有重载了Handler自定义转换操作符,可惜的是,这一点模拟得不是很像,在调用时还必须来一次强制转换才可以:(,具体参看后面的代码:

C++版的MyClass如下:

//MyClass.h

#include "event.h"

//定义EventHandler的函数指针类型

typedef void(*EventHandler)();

class MyClass

{

public:

//构造函数

MyClass(){};

//声明一个事件

event<EventHandler> AEvent;

//激发事件

void FireEvent()

{

if(AEvent != NULL)

{

//C++中必须用EventHandler进行强制类型转换

((EventHandler)AEvent)();

};

}

};

和C#版的MyClass比较一下你就会发现代码非常接近,当然,C#是在语言级直接支持event关键字的,而C++不支持,用模板类代替,所以声明事件的代码有些不一样。还有就是FireEvent()中C++不能把event对象直接当做函数来调用,多了强制类型转换。

C++版的客户代码如下:

//

cppevent.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"

#include "MyClass.h"

//向前声明

void MyEventHandler();

int main(int argc, char* argv[])

{

MyClass Obj;

Obj.AEvent += MyEventHandler;//定制事件

Obj.FireEvent();//这行将导致MyEventHandler被调用

Obj.AEvent -= MyEventHandler;//撤消事件

Obj.FireEvent();//这个将不会引发事件

printf("结束!\n");

char n;

scanf("%c",

&n);

return 0;

}

void MyEventHandler()

{

printf("This

is a event!\n");

}

我们可以看到,可C#版的客户代码相比,核心部分是非常接近的,我们已经可以和C#一样用“+=”和“-=”来定制事件和撤消事件定制,并在Obj的FireEvent()被调用时收到事件通知,输出文本。

鉴于篇幅的原因,我们没有仔细比较两个版本的event的add和remove访问器/成员函数,其实二者也是非常类似的,你可以自己试试。C++版的event的add和remove均为virtual的,你可以从event类继承出来一个MyEvent类,然后重新实现这两个函数,就可以定制自己的add和remove了。这和C#的add/remove访问器的也是非常相像的。

三、总结

通过这场“模仿show”我们可以从更深的层次理解C#的event机制,更重要的是我们用自己所熟悉的东西(C++,模板类)来模仿并解释了我们目前还不太熟悉的东西(C#,event)。

其实,C#的delegate就是C++的函数指针,C# event的核心机制就是C++中的模板(定义event时表现出来)和运算符重载(+=、-=和直接把event当做函数调用)的结合体。C#把C++中容易出错的部分用“新特性”封装了起来,把这部分工作从programmer身上转移到了compiler身上,让我们把更多的精力集中到业务逻辑的处理上。

<完>

袁晓辉

2005年8月2日

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有