CSDN - 专家门诊 -
主  题:
刚接触,大家见笑了。帮忙解释一下C#中的事件机制和委托的概念,谢谢
作  者: BOBSHEN ()
信 誉 值: 100
所属论坛: .NET技术 C#
问题点数: 100
回复次数: 45
发表时间: 2004-1-10 10:54:52

刚看了几天,到了事件那里就有点云山雾罩,那位大虾能给指点一下其中的门道
,能举些简单的例子更好,谢谢
                                   ---------------(说得好开贴再加分!)

回复人: acewang(龍芯*Inside!) ( ) 信誉:163 2004-1-10 11:09:42 得分:0
 

http://www.csdn.net/develop/Article/21/21901.shtm

Top
回复人: delphiseabird(沙鸥) ( ) 信誉:100 2004-1-10 11:13:48 得分:0
 

C#中的消息处理:

在C#中,程序采用了的驱动采用了事件驱动而不是原来的消息驱动,虽然.net框架提供的事件已经十分丰富,但是在以前的系统中定义了丰富的消息对系统的编程提供了方便的实现方法,因此在C#中使用消息有时候还是大大提高编程的效率的。 

 1 定义消息 

  在c#中消息需要定义成windows系统中的原始的16进制数字,比如 

  const int WM_Lbutton = 0x201; //定义了鼠标的左键点击消息 

   public const int USER = 0x0400 // 是windows系统定义的用户消息 

 2 消息发送 

  消息发送是通过windows提供的API函数SendMessage来实现的它的原型定义为 

 [DllImport("User32.dll",EntryPoint="SendMessage")] 
   private static extern int SendMessage( 
          int hWnd,   // handle to destination window 
          int Msg,    // message 
          int wParam, // first message parameter 
          int lParam // second message parameter 
    ); 

3 消息的接受 

 在C#中,任何一个窗口都有也消息的接收处理函数,就是defproc函数 

你可以在form中重载该函数来处理消息 

protected override void DefWndProc ( ref System.WinForms.Message m ) 
{ 
switch(m.msg) 
{ 
case WM_Lbutton : 
 ///string与MFC中的CString的Format函数的使用方法有所不同 
 string message = string.Format("收到消息!参数为:{0},{1}",m.wParam,m.lParam); 
 MessageBox.Show(message);///显示一个消息框 
 break; 
default: 
 base.DefWndProc(ref m);///调用基类函数处理非自定义消息。 
 break; 
} 
} 
其实,C#中的事件也是通过封装系统消息来实现的,如果你在DefWndProc函数中不处理该 
那么,他会交给系统来处理该消息,系统便会通过代理来实现鼠标单击的处理函数,因此你可以通过 
defproc函数来拦截消息,比如你想拦截某个按钮的单击消息 

4 C#中其他的消息处理方法 
  在C#中有的时候需要对控件的消息进行预处理,比如你用owc的spreedsheet控件来处理Excel文件,你不想让用户可以随便选中 
数据进行编辑,你就可以屏蔽掉鼠标事件,这个时候就必须拦截系统预先定义好的事件(这在MFC中称为子类化),你可以通过C#提供的一个接口 
IMessageFilter来实现消息的过滤 
public class Form1: System.Windows.Forms.Form,IMessageFilter 
{ 
 const int WM_MOUSEMOVE = 0x200 
 public bool PreFilterMessage(ref Message m)  
 {  Keys keyCode = (Keys)(int)m.WParam & Keys.KeyCode;  
   if(m.Msg == m.Msg==WM_MOUSEMOVE) //||m.Msg == WM_LBUTTONDOWN 
   { 
    //MessageBox.Show("Ignoring Escape...");   
    return true;  
   }  
    return false;  
 } 
}


Top
回复人: renrenqq(ddlly) ( ) 信誉:100 2004-1-10 11:24:50 得分:0
 

委托是用来把方法当作参数来传递的,委托的签名(参数)必须和所委托的方法一致。

使用事件的步骤
1。声明一个委托
public delegete void delegateME();
2。声明此委托的事件
public event delegateME eventME;
3。把方法订阅到一个事件
eventME+=new delegete(objA.Method);
括号里传递的是需要订阅事件的方法。


然后在别处运行eventME将调用objA.Method方法。

如果想进一步了解C#中的事件处理机制,编写自己的事件可参看
http://www.csdn.net/develop/Read_Article.asp?Id=22951

Top
回复人: polarlm(今年本命年) ( ) 信誉:100 2004-1-10 11:32:15 得分:0
 

代表使得这样的方案变为可能:其他语言-C++, Pascal, Modula 等可以用功能指针来定位。与C++的功能指针不同,代表完全是面向对象的;与C++指向成员功能不同,代表把一个对象实例和方法都进行封装。 一个代表声明定义了一个从类System.Delegate 延伸的类。一个代表实例封装一个方法,可调用实体。对于实例方法,一个可调用实体由一个实例和一个实例中的方法组成。对于静态方法,一个可调用实体完全只是由一个方法组成。如果你有一个代表实例和一个适当的参数集合,你就可以用参数来调用这个代表。 代表的一个有趣而又有用的特性是它不知道或不关心它引用的对象的类。只要方法的签名与代表的签名一致,任何对象都可以作。这使得代表适合作“匿名“调用”。

Top
回复人: polarlm(今年本命年) ( ) 信誉:100 2004-1-10 11:35:11 得分:0
 

一个事件是一个使对象或类可以提供公告的成员。用户可以通过提供事件句柄来为事件添加可执行代码。在包含一个事件成员声明的类或结构的程序文字中,事件成员与代表类型的私有域或属性相关,而这个成员可以用于任何允许使用域或属性的上下文中。 如果一个类或结构的程序文字外面包含一个事件成员声明,这个事件成员就只能被作为+= 和 -= 操作符 的右手操作数使用。这些操作符被用来为事件成员附加或去掉事件句柄,而这个事件成员的访问操作符控制操作在其中被执行的上下文。 由于+= 和 -= 是唯一可以在声明了事件成员的类型外的事件上使用的操作,外部代码可以为一个事件添加或去掉句柄,但是不能通过任何其他途径获得或修改基本事件域或事件属性的数值。

Top
回复人: Sunmast(MDA is on the way..) ( ) 信誉:100 2004-1-10 11:36:09 得分:0
 

http://blog.sunmast.com/sunmast/posts/230.aspx

或者在CSDN搜索下"事件",或者"委托",肯定要出来一大堆

Top
回复人: BOBSHEN() ( ) 信誉:100 2004-1-10 11:42:15 得分:0
 

谢谢各位的回复,我得先消化一下各位提供的资料

楼上所说的“代表”是不是就是我所说的"委托"?
>>一个代表声明定义了一个从类System.Delegate 延伸的类

Top
回复人: cqnimin() ( ) 信誉:100 2004-1-10 11:52:08 得分:0
 

事件是可以通过代码响应或“处理”的操作。事件可由用户操作(如单击鼠标或按某个键)、程序代码或系统生成。

事件驱动的应用程序执行代码以响应事件。每个窗体和控件都公开一组预定义事件,您可根据这些事件进行编程。如果发生其中一个事件并且在相关联的事件处理程序中有代码,则调用该代码。

对象引发的事件类型会发生变化,但对于大多数控件,很多类型是通用的。例如,大多数对象都处理 Click 事件,即如果用户单击窗体,将执行该窗体的 Click 事件处理程序中的代码。

注意 很多事件与其他事件一起发生。例如,在发生 DoubleClick 事件的过程中,还发生 MouseDown、MouseUp 和 Click 事件。
委托及其角色
委托是 .NET 框架内的常用类,用于生成事件处理机制。委托大体上相当于常用于 C++ 和其他面向对象的语言中的函数指针。但与函数指针不同的是,委托是面向对象的、类型安全的和保险的。另外,函数指针只包含对特定函数的引用,而委托由对对象的引用以及对该对象内一个或多个方法的引用组成。

此事件模型使用“委托”将事件绑定到用来处理它们的方法。委托允许其他类通过指定处理程序方法注册事件通知。当发生事件时,委托调用绑定的方法。

委托可绑定到单个方法或多个方法,后者又称为多路广播。当创建事件的委托时,您(或“Windows 窗体设计器”)通常创建多路广播事件。极少的例外情况是,某个事件会导致特定过程(如显示对话框),而该过程在逻辑上不在每个事件中重复多次。

多路广播委托维护它所绑定到的方法的调用列表。多路广播委托支持将方法添加到调用列表的 Combine 方法以及移除它的 Remove 方法。 

当应用程序记录某个事件时,控件通过调用该事件的委托引发事件。委托接着调用绑定的方法。最常见的情况(多路广播委托)是,委托依次调用调用列表中的每个绑定方法,这样可提供一对多通知。此策略意味着控件不需要维护事件通知的目标对象的列表,即委托处理所有注册和通知。 

委托也允许将多个事件绑定到同一个方法,从而允许多对一通知。例如,单击按钮事件和单击菜单命令事件可调用同一委托,然后该委托调用单个方法以相同方式处理各个事件。

委托使用的绑定机制是动态的,即委托可在运行时绑定到其签名与事件处理程序的签名匹配的任何方法。此功能使您得以根据条件设置或更改绑定的方法,并动态地将事件处理程序附加到控件上。
ms-help://MS.NETFrameworkSDK.CHS/cpguidenf/html/cpconWhatIsEvent.htm
这是2002的sdk文档

Top
回复人: polarlm(今年本命年) ( ) 信誉:100 2004-1-10 12:09:06 得分:0
 

楼上所说的“代表”是不是就是我所说的"委托"?
>>一个代表声明定义了一个从类System.Delegate 延伸的类


正是!

Top
回复人: qazsw(兴华) ( ) 信誉:100 2004-1-10 13:03:33 得分:0
 

up

Top
回复人: BOBSHEN() ( ) 信誉:100 2004-1-10 13:08:08 得分:0
 

再次感谢各位的热心帮忙!
    先解释一下:在下来论坛的次数不多,所以论坛的有些功能不是很熟,比如搜索,我都不知道在那里,让大家见笑了,请大家顺手指点一下。另外想问一下论坛有没有离线资料包和阅读器下载,谢谢。
    回到问题中,举一个例子来说,比如要设计一个button类,并要使这个类的实例能响应onclick事件, 我这样做:
   1、在 button类中申明一个委托类
      public delegate void myclick(object sender);
   2、在 button类中申明一个onclick事件,这个事件是上边委托类的实例
      public event  myclick onclick;
-----------在button类中作这两步是不是就齐活了?(语法不一定对,就这个意思了)

   另外有一个form对象中创建了一个上边的button的实例button1,而且要在单击button1调用这个form的一个方法  public void button1click(object sender);,也就是把form的button1click方法注册到button1的onclick事件,是不是在form的构造方法中
    button1.onclick+=button1click  (这里不对吧,应该怎么做?)

---------------------我上边的理解肯定有问题吧,大虾们给指导一下。

Top
回复人: BOBSHEN() ( ) 信誉:100 2004-1-10 13:32:55 得分:0
 

button1.onclick+=new delegete(this.button1click) 
  这样吗?好像也不对吧

Top
回复人: allen1981813(MS FANS 小新) ( ) 信誉:100 2004-1-10 13:50:59 得分:0
 

楼上的,你的问题在03里是不可以的,04开始支持了.

另外事件是代理的集合(数学上理解的)执行时候是以循环联表的形式的.至于里面做啥,....MS知道

Top
回复人: BOBSHEN() ( ) 信誉:100 2004-1-10 14:02:03 得分:0
 

我的问题是:
  1、我上边在button类中申明onclick事件的做法对不对?
  2、我在form中把button1click方法注册到button1的onclick事件的做法对不对?
  另外,楼上所说的本来是我搞清楚基本概念之后接下来要问的:
  事件的调用列表是一什么样的形式放在那里的?

Top
回复人: liulovetoo(小帅哥有大智慧) ( ) 信誉:96 2004-1-10 14:37:50 得分:0
 

委托,说白了就是个函数指针。也就是回调函数

Top
回复人: BOBSHEN() ( ) 信誉:100 2004-1-10 17:11:17 得分:0
 

大侠们,是时候出手了!!!

Top
回复人: IceboundRock() ( ) 信誉:100 2004-1-10 21:04:38 得分:0
 

不是函数指针,因为它是类型安全的,更接近STL中的仿函数,或者叫做函数对象

Top
回复人: lyhold(让你飞) ( ) 信誉:100 2004-1-11 16:26:30 得分:0
 

up!

Top
回复人: TomMax(笑望人生) ( ) 信誉:100 2004-1-12 8:38:57 得分:0
 

实际上,委托很容易理解。委托就是在条件成立的时候触发一个事件,从而可以调用一段代码。这个概念可以转换到现实中来。

比如说一个公司(场景),你是老板,手下有两个员工,小张和小李。你命令小张注意小李,在开发软件工作的时候如果上网打游戏,你就记录下来,从小李工资里扣100元钱。这个实际上就是现实中的委托。

这个实际上就是委托,编写程序的就是老板,运行时就是场景,小张和小李就是两个对象,小张上网打游戏就是对象的一个方法,你指定小李注意上网打游戏就是你定义一个事件,你指定小李扣100元钱就是小李对象的一个方法,这个方法在事件发生的时候自动被调用。

所以委托必须有三个要素:
1 激发事件的对象,就是小张。
2 定义事件,就是你要让小李监视。
3 事件发生后执行的方法,就是让小李扣小张100元钱。

在程序中定义好上面三个要素后,实际上你就定义了一个委托。

Top
回复人: TomMax(笑望人生) ( ) 信誉:100 2004-1-12 8:53:32 得分:0
 

如果上面我说的不清楚,我用代码演示一遍:
一个现实中的例子:
小王委托小张帮他监视小李,只要他一跳舞就去揍他,呵呵,好吧,我就用这个例子来说说C#中的DELEGATE,现在有三个类,呵呵,是小王,小张,小李。那么小张就是委托。
下面例子

public class 小李
{
    public 小李(小张 obj)
    {
        // 构造器,传递小张的实例进来
    }

    // 下面是小李的跳舞方法
    public void 跳舞()
    {
        // 跳舞方法
        把这个方法传递给小张
        实例小张(this);
    }
}

下面我们来定义委托小张。
// 定义参数,把小李给传递进来,下面的object实际上就是小李的实例。
public delegate 小张(object sender);

下面定义小王,小王不会别的,就会打人。:-)
public class 小王
{
    public 小王
    {
       // 构造器
    }
    
    // 小王打人方法
    public void 揍(object obj) // obj参数就是要打的对象
    {
        // 我打,我打打打!!
    }
}

下面是场景
public class 舞厅
{
    public int main()
    {
        new 实例小王;

        new 实例小张(小王.揍);
        \\这句是小王委托小张去监视小李
        new 实例小李(小张);
        
        实例小李.跳舞; 
        // 小李一跳舞,那就要被打了。:-)
    }
}

Top
回复人: Tadpole0510(学习、学习) ( ) 信誉:100 2004-1-12 9:08:36 得分:0
 

楼上写的太精彩了,也太通俗了,好好。。。。

Top
回复人: BOBSHEN() ( ) 信誉:100 2004-1-12 9:47:34 得分:0
 

非常感谢 TomMax(笑望人生) 我就要这样的例子!
不过在你的例子中在下还有一些疑问,请再指教一下:
1、    public 小李(小张 obj)
       {
        // 构造器,传递小张的实例进来
       }
    --------------这里,委托小张的实例是在外边创建,然后通过小李的构造方法传入吗?
                  我觉得应该在小李的构造方法中创建,不知我的理解对不对?
2、如果我还要在小李跳舞的时候,执行 小赵.关门,小钱.放狗...怎么作? 也就是有多个对象监视小李跳舞,并采取相应的措施?

Top
回复人: tjq_tang(过河兵) ( ) 信誉:99 2004-1-12 10:07:05 得分:0
 

委托/事件 前者是主动执行,后者是被动执行。
事件得需什么东西激活它,而委托则是主动执行的.这个是个相对的概念.

委托有点类似于VC的callback.

搂主可以看看delegate的一般使用就能够体会到的.

Top
回复人: BOBSHEN() ( ) 信誉:100 2004-1-12 10:29:14 得分:0
 

如果我给一个委托类的实例注册了多个方法,是不是在这个实例中会维护一个调用列表?类方法和静态方法可以共存于同一个调用列表吗?能不能访问这个调用列表来获得一个委托类实例都注册了那些方法及这些方法的相关信息?
           ----顺便问一句,vc的事件也用的是callback机制吗?我对vc一窍不通。

Top
回复人: nova2001(禁飞区) ( ) 信誉:100 2004-1-12 11:15:20 得分:0
 

UP!

Top
回复人: lanyahuhu(宝姐姐和林妹妹,两个我都喜欢(补钙中)) ( ) 信誉:79 2004-1-12 11:18:04 得分:0
 

up

Top
回复人: lyhold(让你飞) ( ) 信誉:100 2004-1-12 14:00:01 得分:0
 

TomMax(笑望人生)
讲的很精彩,鼓掌!!!

Top
回复人: BOBSHEN() ( ) 信誉:100 2004-1-12 14:19:47 得分:0
 

up!
   TomMax(笑望人生) 讲的确实很精彩,但是取不见得对(我认为 : )  也许是我理解错了)
请大侠回来再指点一二,谢谢


Top
回复人: turnmissile(会翻跟头的导弹) ( ) 信誉:100 2004-1-12 14:21:52 得分:0
 

even是delegate的一个集合?这个我还是不够明白,是不是说一个even定义的对象中可以+=多个delegate??如果如此,那么怎么调用到这个delegate呢?

我怎么不明白event的存在意义
感觉好像又没有他都可以阿!



Top
回复人: liduke(天下有雪) ( ) 信誉:100 2004-1-13 18:24:33 得分:0
 

TomMax(笑望人生)
讲的很精彩,鼓掌!!!

Top
回复人: hddhddhdd(还刀的) ( ) 信誉:100 2004-1-13 18:56:07 得分:0
 

好帖子!

希望继续讨论,最好置顶

Top
回复人: wuye(午夜寻欢(QQ:332888107)) ( ) 信誉:90 2004-6-16 15:41:17 得分:0
 

up

Top
回复人: athossmth(athos) ( ) 信誉:100 2004-6-16 16:32:19 得分:0
 

这样的贴子要顶

Top
回复人: kwork(说好不打脸) ( ) 信誉:100 2004-6-16 17:00:54 得分:0
 

不错的帖子,标记

Top
回复人: ILoveProgramer(骆驼) ( ) 信誉:100 2004-06-23 17:54:00 得分:0
 
这种贴子不置顶简直就是......
Top
回复人: seekg() ( ) 信誉:100 2004-06-23 18:05:00 得分:0
 
mark
Top
回复人: clearma(可乐马) ( ) 信誉:100 2004-06-26 01:34:00 得分:0
 
收藏
Top
回复人: xjjdanran(何流) ( ) 信誉:100 2004-06-26 09:39:00 得分:0
 
学习,UP
Top
回复人: quakecs(@_@) ( ) 信誉:68 2004-07-12 11:12:00 得分:0
 
up
Top
回复人: quakecs(@_@) ( ) 信誉:68 2004-07-12 11:31:00 得分:0
 
TomMax(笑望人生)
的例子中没有使用到 event,
可否再用同样的方式阐述一下。
Top
回复人: orbitbd(大天二) ( ) 信誉:100 2004-07-12 14:11:00 得分:0
 
委托就是一种方法容器,是方法指针的peer。
通俗一点就像马桶,可以不同的人坐上去,排泄物可以通到不同的地方,但是屁股必须要和马桶尺寸完全一样
Top
回复人: TomMax(笑望人生) ( ) 信誉:100 2004-07-12 18:11:00 得分:0
 
呵呵,好长时间没有看这个帖子,好吧,我重新说一次。
还是上次的那个例子:

比如说一个公司(场景),你是老板,手下有两个员工,小张和小李。你命令小张注意小李,在开发软件工作的时候如果上网打游戏,你就记录下来,从小李工资里扣100元钱。这个实际上就是现实中的委托。

现在给出一个代码,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);
}


Top
回复人: zhjh123(jianhua) ( ) 信誉:100 2004-07-13 18:34:00 得分:0
 
up
Top
回复人: phoenixsharp(小星星) ( ) 信誉:100 2004-07-13 18:46:00 得分:0
 
TO: TomMax(笑望人生)  精彩!!!!!!!!学习中.........
Top
回复人: cdzjhmao() ( ) 信誉:100 2004-07-13 19:29:00 得分:0
 
学习
Top