CSDN - 专家门诊 -
主  题:
“.net百题问答的活动”--许多人问过的--《 C#委托及事件 》
作  者: TomMax (笑望人生)
信 誉 值: 100
所属论坛: .NET技术 C#
问题点数: 20
回复次数: 62
发表时间: 2004-07-13 07:52:33
有许多人问的,.Net中的委托以及事件处理。我拿简单的例子说明一下,是现实中的例子:

比如说一个公司(场景),你是老板,手下有两个员工,小张和小王。
你命令小王,如果小张玩游戏,则小王扣去小张500元钱。

这就是现实中的委托。

实际上,在写程序中,程序员就是老板,小张和小王就是两个对象。小张玩游戏是一个方法,小张还有一个游戏事件,他玩游戏激发这个事件。而小王就是事件处理对象,他负责把小张的钱扣除500。

所以,委托有如下几个要素:
1 激发事件的对象--就是小张
2 处理对象事件的对象--就是小王
3 定义委托,就是你让小王监视小张。

如果这三个要素都满足的话,则你就写出了一个完整事件的处理。

下面有个例子:在vs.net2003 C#控制台应用程序编辑运行成功:
using System;

namespace CSharpConsole
{
    public class 场景
    {
        [STAThread]
        public static void Main(string[] args)
        {
	Console.WriteLine("场景开始了....");
	// 生成小王
	小王 w = new 小王();
	// 生成小账
	小张 z = new 小张();

	// 指定监视
	z.PlayGame += new PlayGameHandler(w.扣钱);
			
	// 开始玩游戏
	z.玩游戏();

	Console.WriteLine("场景结束...");
	Console.ReadLine();
         }
    }



    // 负责扣钱的人
    public class 小王
    {
	public 小王()
	{
              Console.WriteLine("生成小王...");
	}

	public void 扣钱(object sender,EventArgs e)
	{
		Console.WriteLine("小王:好小子,上班时间胆敢玩游戏...");
		Console.WriteLine("小王:看看你小子有多少钱...");
		小张 f = (小张)sender;
		Console.WriteLine("小张的钱: " + f.钱.ToString());
		Console.WriteLine("开始扣钱......");
		System.Threading.Thread.Sleep(500);
		f.钱 = f.钱 - 500;
		Console.WriteLine("扣完了....现在小张还剩下:" + f.钱.ToString());
	}
    }

    // 如果玩游戏,则引发事件
    public class 小张
    {
	// 先定义一个事件,这个事件表示“小张”在玩游戏。
         public event PlayGameHandler PlayGame;
	// 保存小张钱的变量
	private int m_Money;

	public 小张()
	{
		Console.WriteLine("生成小张....");
		m_Money = 1000; // 构造函数,初始化小张的钱。
	}

	public int 钱 // 此属性可以操作小张的钱。
	{
		get
		{
			return m_Money;
		}
		set
		{
			m_Money = value;
		}
	}

	public void 玩游戏()
	{
		Console.WriteLine("小张开始玩游戏了.....");
		Console.WriteLine("小张:CS好玩,哈哈哈! 我玩.....");
		System.Threading.Thread.Sleep(500);
		System.EventArgs e = new EventArgs();
		OnPlayGame(e);
	}

	protected virtual void OnPlayGame(EventArgs e)
	{
		if(PlayGame != null)
		{
			PlayGame(this,e);
		}
	}
    }

    // 定义委托处理程序
    public delegate void PlayGameHandler(object sender,System.EventArgs e);

}

回复人: tenglong2004() ( ) 信誉:100 2004-07-13 08:02:00 得分:0
 
说的好,鼓掌!
Top
回复人: mythursday888() ( ) 信誉:100 2004-07-13 08:13:00 得分:0
 
good!parapara!
Top
回复人: 91bct(路人) ( ) 信誉:100 2004-07-13 08:23:00 得分:0
 
说的好
Top
回复人: petiteturbo(turbo) ( ) 信誉:97 2004-07-13 08:35:00 得分:0
 
学习!!!

Top
回复人: SoRoMan(因为所以,所以因为) ( ) 信誉:100 2004-07-13 08:49:00 得分:0
 
不错!
对于事件机制,C#提供了语法上的甜头,却造成了理解上的麻烦。
Top
回复人: TomMax(笑望人生) ( ) 信誉:100 2004-07-13 08:50:00 得分:0
 
大家想一下,如果还有一个小李,如果老板指定:
如果小张玩游戏,不但小王扣小张500元钱,而且让小李揍小张,那么代码怎么写呢?
Top
回复人: xuhx(一觉亮天) ( ) 信誉:100 2004-07-13 08:52:00 得分:0
 
理解上的麻烦,可不可以解释一下
Top
回复人: ShengNet(打败.net) ( ) 信誉:100 2004-07-13 08:53:00 得分:0
 
学习一下.......
Top
回复人: ydx(只要你决心成功,失败永远不会把你击垮!) ( ) 信誉:99 2004-07-13 09:01:00 得分:0
 
study
Top
回复人: rainy6669() ( ) 信誉:100 2004-07-13 09:01:00 得分:0
 
我认可楼主 得实现一下
Top
回复人: chNET(有神论者) ( ) 信誉:100 2004-07-13 09:02:00 得分:0
 

 up


Top
回复人: SaVinWen(三文) ( ) 信誉:100 2004-07-13 09:14:00 得分:0
 
up
study!~
Top
回复人: fgc5201314(成成(努力中...)) ( ) 信誉:100 2004-07-13 09:14:00 得分:0
 
good
Top
回复人: anantnt203120(阿南) ( ) 信誉:100 2004-07-13 09:23:00 得分:0
 
谢谢大家对我们菜鸟类的关心,程序中有几点还不太明白:
扣钱事件中:	
小张 f = (小张)sender;  //这是什么声明?
System.Threading.Thread.Sleep(500);   //起什么作用?
玩游戏事件中:
System.Threading.Thread.Sleep(500);
System.EventArgs e = new EventArgs();
OnPlayGame(e);
能解释一下吗?
Top
回复人: iamsoloist(Soloist) ( ) 信誉:100 2004-07-13 09:25:00 得分:0
 
写得太好了,不过罚点钱就行了,就不用揍了吧!
Top
回复人: brightheroes(闭关|重剑无锋 大巧不工) ( ) 信誉:98 2004-07-13 09:36:00 得分:0
 
支持原创
生动活泼

Top
回复人: xuguchu(南极星) ( ) 信誉:100 2004-07-13 09:37:00 得分:0
 
先up!
正好看不明白的說
打印看先
謝謝!
Top
回复人: forideal(我心飞翔) ( ) 信誉:100 2004-07-13 09:44:00 得分:0
 
经典,支持,收藏
Top
回复人: upflare(将进酒) ( ) 信誉:100 2004-07-13 09:51:00 得分:0
 
回复人: forideal(我心飞翔) ( ) 信誉:100  2004-07-13 09:44:00  得分: 0  
 
 
   经典,支持,收藏
  
 
================
up
Top
回复人: TomMax(笑望人生) ( ) 信誉:100 2004-07-13 10:04:00 得分:0
 
回楼上:
扣钱事件中:	
小张 f = (小张)sender;

这个代码表示让小王扣小张的钱,那么,小王扣钱的时候,必须要操作小张的对象实例。
如果大家仔细看代码,就会明白,在小王扣钱的方法里面:
public void 扣钱(object sender,EventArgs e)
方法参数里面有个object sender对象,这个对象表示激发事件的对象,在这个程序里面就表示这个sender其实是小张,但是传递的对象类型是object,所以必须使用对象类型转换语句把sender从object转成小张,所以有了:
小张 f = (小张)sender;

System.Threading.Thread.Sleep(500);
这个代码是让程序暂停半秒钟,500的单位是毫秒,500毫秒就是半秒。

在小张类里面,有一行代码
protected virtual void OnPlayGame(EventArgs e)
这个方法就是引发事件的方法,因为PlayGame事件是小张的事件,那么为了引发这个事件,必须指定一个引发事件的方法,所以,在小张类里面的
public void 玩游戏()
方法里,就有了
OnPlayGame(e);

Top
回复人: redbb(....Dotneter....) ( ) 信誉:156 2004-07-13 10:10:00 得分:0
 
support
Top
回复人: TomMax(笑望人生) ( ) 信誉:100 2004-07-13 10:11:00 得分:0
 
还有,我在上面说的:
-----------------
大家想一下,如果还有一个小李,如果老板指定:
如果小张玩游戏,不但小王扣小张500元钱,而且让小李揍小张,那么代码怎么写呢?
-----------------

这个请大家考虑一下,这个实际上是.Net的组合委托(多路广播委托)的使用原理,就是当一个对象激发事件后,两个(或者多个)对象都可以按照自己的方法处理这个事件。
Top
回复人: jupiterII() ( ) 信誉:100 2004-07-13 10:25:00 得分:0
 
呵呵,非常有意思,up一下.
Top
回复人: zhyx21century(双手捧着msdn,双眼泪水潺潺而下) ( ) 信誉:95 2004-07-13 10:31:00 得分:0
 
public void 玩游戏()
	{
		......
		System.EventArgs e = new EventArgs();
		OnPlayGame(e);//为什么不直接PlayGame(this,e)?;
而且我觉得PlayGame只是申明了一次,为什么不是null
	}

谢谢
	
Top
回复人: wuchangyu(bare) ( ) 信誉:100 2004-07-13 10:42:00 得分:0
 
很好
Top
回复人: whmjw(找工作中) ( ) 信誉:100 2004-07-13 11:04:00 得分:0
 
说的好
Top
回复人: yunicorn(yunicorn) ( ) 信誉:98 2004-07-13 11:05:00 得分:0
 
起立鼓掌,非常好
Top
回复人: aderly(美女需要.net) ( ) 信誉:96 2004-07-13 13:48:00 得分:0
 
大家想一下,如果还有一个小李,如果老板指定:
如果小张玩游戏,不但小王扣小张500元钱,而且让小李揍小张,那么代码怎么写呢?

樓主告訴我們答案吧,我想不出來:(
Top
回复人: hai4(敏敏) ( ) 信誉:100 2004-07-13 14:22:00 得分:0
 
就是一个Observer模式
Top
回复人: brightheroes(闭关|那一剑的风情) ( ) 信誉:98 2004-07-13 14:40:00 得分:0
 
public class 小李
    {
	public 小李()
	{
              Console.WriteLine("生成小李...");
	}

	public void 揍人(object sender,EventArgs e)
	{
		Console.WriteLine("小李:好小子,上班时间胆敢玩游戏...");
		Console.WriteLine("小李:正好我手痒痒...");
		Console.WriteLine("开始揍小张......");                   
		System.Threading.Thread.Sleep(500);
	         Console.WriteLine("揍完了,小张现在鼻青眼肿");
	}
    }

Top
回复人: brightheroes(闭关|那一剑的风情) ( ) 信誉:98 2004-07-13 14:42:00 得分:0
 
// 生成小李
	小李 li = new 小李();


// 指定监视
z.PlayGame += new PlayGameHandler(w.扣钱);
	z.PlayGame += new PlayGameHandler(li.揍人);

Top
回复人: TomMax(笑望人生) ( ) 信誉:100 2004-07-13 14:53:00 得分:0
 
To  brightheroes(闭关|那一剑的风情):
厉害厉害,这么快就出来了。
哈哈,兄台水平厉害!
Top
回复人: chinawn(chinawn) ( ) 信誉:98 2004-07-13 14:58:00 得分:0
 
hao  dong dong
Top
回复人: pclogic(半边伞) ( ) 信誉:100 2004-07-14 07:43:00 得分:0
 
非常好!
Top
回复人: telantlan(宇翔) ( ) 信誉:100 2004-07-14 09:05:00 得分:0
 
刚刚看到这里,贴出Professonal C# 2nd Edition的代码.
using System;
using System.Windows.Forms;

namespace UserInputNotify
{
	enum RequestType {AdRequest, PersonalMessageRequest};

	class UserRequestEventArgs : EventArgs
	{
		private RequestType request;

		public UserRequestEventArgs(RequestType request) : base()
		{
			this.request = request;
		}

		public RequestType Request
		{
			get 
			{
				return request;
			}
		}
	}

	class UserInputMonitor
	{
		public delegate void UserRequest(object sender, UserRequestEventArgs e);

		public event UserRequest OnUserRequest;

		public void Run()
		{
			bool finished = false;

			do 
			{
				Console.WriteLine("Select preferred option:");
				Console.WriteLine(" Request advertisement - hit A then Return ");
				Console.WriteLine(" Request personal message from Mortimer - hit P then Return ");
				Console.WriteLine(" Exit - hit X then Return\n>\b\b"); 

				string response = Console.ReadLine();
				char responseChar = (response=="") ? ' ' : char.ToUpper(response[0]);

				switch (responseChar)
				{
					case 'X':
						finished = true;
						break;
					case 'A':
						OnUserRequest(this, new UserRequestEventArgs(RequestType.AdRequest));
						break;
					case 'P':
						OnUserRequest(this, new UserRequestEventArgs(RequestType.PersonalMessageRequest));
						break;
				}
			}while (!finished);
		}
	}

	class MessageDisplayer
	{
		public MessageDisplayer(UserInputMonitor monitor)
		{
			monitor.OnUserRequest += new UserInputNotify.UserInputMonitor.UserRequest(UserRequestHandler);
		}

		protected void UserRequestHandler(object sender, UserRequestEventArgs e)
		{
			switch (e.Request)
			{
				case RequestType.AdRequest:
					Console.WriteLine("Mortimer Phone is better than anyone else because all our software is written in C#!\n");
					break;
				case RequestType.PersonalMessageRequest:
					Console.WriteLine("Today Mortimer issued the following statement:\n   Nevermore!\n");
					break;
			}
		}
	}

	class ManagersStaffMonitor
	{
		public ManagersStaffMonitor(UserInputMonitor monitor)
		{
			monitor.OnUserRequest += new UserInputNotify.UserInputMonitor.UserRequest(UserRequestHandler);
		}

		protected void UserRequestHandler(object sender, UserRequestEventArgs e)
		{
			if (e.Request == RequestType.PersonalMessageRequest)
			{
				MessageBox.Show("^_^WA ka ka","Mortimer says ......");
			}
		}
	}
	/// <summary>
	/// Class1 的摘要说明。
	/// </summary>
	class MainClass
	{
		/// <summary>
		/// 应用程序的主入口点。
		/// </summary>
		[STAThread]
		static void Main(string[] args)
		{
			UserInputMonitor inputMonitor = new UserInputMonitor();
			MessageDisplayer inputProccessor = new MessageDisplayer(inputMonitor);
			ManagersStaffMonitor mortimer = new ManagersStaffMonitor(inputMonitor);

			inputMonitor.Run();

		}
	}
}

Top
回复人: comy(软件民工) ( ) 信誉:98 2004-07-14 09:17:00 得分:0
 
说的很好,鼓掌!
应该属于设计模式的observer模式吧




----------------------------------------------------------------------
欢迎试用ASP.NET大文件上传组件(AspnetUpload 1.0 Release & 无刷新进度条)
http://bestcomy.europe.webmatrixhosting.net
----------------------------------------------------------------------
Top
回复人: telantlan(宇翔) ( ) 信誉:100 2004-07-14 09:27:00 得分:0
 
要注意里面的引用.

Top
回复人: zl2006(巧郁) ( ) 信誉:100 2004-07-14 09:30:00 得分:0
 
关于还有其它员工,你可用属性吧!把小张类改为person类,表示被监视的员工,
增加name属性,利用构造函数去设置它的姓名,这样应该就可以了吧!

或

建立一个抽象类,下面的定义一此子类.........
Top
回复人: zl2006(巧郁) ( ) 信誉:100 2004-07-14 09:36:00 得分:0
 
对了,你的程序有很大的缺陷,
打个比方,假设小王要监视整个工厂的员工,而工厂有几百人,
那你不是每个员工都要生成一个类吗,
并且要指定多少个监视呀!
建议你把员工定义为一个抽象类,所有员工统一处理.........
Top
回复人: mgh34429245(mgh) ( ) 信誉:100 2004-07-14 09:48:00 得分:0
 
好!
Top
回复人: good2000(break) ( ) 信誉:100 2004-07-14 09:53:00 得分:0
 
support,good,up
Top
回复人: harisonh2l(韩) ( ) 信誉:100 2004-07-14 10:20:00 得分:0
 
shou cang
Top
回复人: chinawn(chinawn) ( ) 信誉:98 2004-07-14 10:39:00 得分:0
 
经典~
CSDN里的周星弛~
呵呵
Top
回复人: Jorcks2002(Jorcks2002) ( ) 信誉:81 2004-07-14 11:07:00 得分:0
 
不错,打个记号先!
Top
回复人: shepengtao(爱花) ( ) 信誉:98 2004-07-14 13:51:00 得分:0
 
好东东
Top
回复人: TeaBall(茶包★☆回归☆★) ( ) 信誉:100 2004-07-14 13:58:00 得分:0
 
好帖!不顶不行呀!
Top
回复人: czhenq(挨饿中,找份兼职) ( ) 信誉:100 2004-07-14 14:03:00 得分:0
 
up
Top
回复人: liulovetoo(小帅哥有大智慧) ( ) 信誉:96 2004-07-14 14:22:00 得分:0
 
好帖!不顶不行呀!
Top
回复人: wangrong001(wr001) ( ) 信誉:100 2004-07-14 17:21:00 得分:0
 
太好了,顶啊,顶啊,大家快来顶!
Top
回复人: superhasty(鸟儿自空中飞过) ( ) 信誉:94 2004-07-15 08:37:00 得分:0
 
good,讲得挺精彩!
Top
回复人: jia4950() ( ) 信誉:100 2004-07-15 09:22:00 得分:0
 
佩服,学习中……
Top
回复人: forideal(我心飞翔) ( ) 信誉:100 2004-07-15 09:36:00 得分:0
 
这帖子应该置顶的呀,怀疑版主的工作热情
Top
回复人: chang110cn(口号) ( ) 信誉:98 2004-07-15 09:58:00 得分:0
 
形象,生动。
Top
回复人: Zoujinyucn(不会游泳的鱼) ( ) 信誉:99 2004-07-15 10:30:00 得分:0
 
支持
Top
回复人: luckyprg(城市猫) ( ) 信誉:100 2004-07-15 11:40:00 得分:0
 
tnnd.8C8C!
Top
回复人: meilian01(meilian) ( ) 信誉:100 2004-07-15 17:06:00 得分:0
 
up
Top
回复人: hiying(黑鹰) ( ) 信誉:100 2004-07-18 16:53:00 得分:0
 
太棒了,希望搞手多写点这样的例子,一看就明白,哈哈

顶
Top
回复人: mediar() ( ) 信誉:99 2004-07-23 22:33:00 得分:0
 
dddddddddddddd
Top
回复人: hxhbluestar(贺星河) ( ) 信誉:97 2004-07-23 22:44:00 得分:0
 
希望每个难点都有这样的例子就好了
Top
回复人: Sunmast(速马, MayFlower2.x) ( ) 信誉:95 2004-07-23 23:34:00 得分:0
 
我这还有一个例子

using System;

namespace Sunmast.Sample.异步编程示例
{
 public delegate void 火警通报者(object 通报者, 火灾事件类 火灾);

 public class 火灾事件类: EventArgs
 {
  public string 房间;
  public int 危害级别;

  public 火灾事件类(string r, int i)
  {
   this.房间 = r;
   this.危害级别 = i;
  }
 }

 public class 火警类
 {
  public event 火警通报者 报告者;

  public void 激活火警(string r,int i)
  {
   火灾事件类 一个火灾 = new 火灾事件类(r,i);
   if(报告者 != null)
    报告者("Sunmast(速马)",一个火灾);
  }
 }

 public class 火警处理类
 {
  public 火警处理类(火警类 火警)
  {
   火警.报告者 += new 火警通报者(处理火灾);
  }

  private void 处理火灾(object 发送者, 火灾事件类 火灾)
  {
   Console.WriteLine("这个火警由{0}发出,发生地点: {1}, 火警级别: {2}",发送者.ToString(),火灾.房间,火灾.危害级别);
  }
 }

 public class Sample
 {
  [STAThread]
  static void Main(string[] args)
  {
   火警类 这个火警 = new 火警类();
   火警处理类 火灾处理 = new 火警处理类(这个火警);

   这个火警.激活火警("不晓得",5);// 好了,激活火警了,怎么处理,上面都定义了

   Console.ReadLine();
  }
 }
}

http://blog.sunmast.com/sunmast/archive/2003/12/27/230.aspx
Top