C#中的“代理”和“事件”

事件(event)是一个非常重要的概念,我们的程序时刻都在触发和接收着各种事件:鼠标点击事件,键盘事件,以及处理操作系统的各种事件。所谓事件就是由某个对象发出的消息。比如用户按下了某个按钮,某个文件发生了改变,socket上有数据到达。触发事件的对象称作发送者(sender),捕获事件并且做出响应的对象称作接收者(receiver),一个事件可以存在多个接受者。 在异步机制中,事件是线程之间进行通信的一个非常常用的方式。比如:用户在界面上按下一个按钮,执行某项耗时的任务。程序此时启动一个线程来处理这个任务,用户界面上显示一个进度条指示用户任务执行的状态。这个功能就可以使用事件来进行处理。可以将处理任务的类作为消息的发送者,任务开始时,发出“TaskStart”事件,任务进行中的不同时刻发出“TaskDoing”事件,并且携带参数说明任务进行的比例,任务结束的时候发出“TaskDone”事件,在画面中接收并且处理这些事件。这样实现了功能,并且界面和后台执行任务的模块耦合程度也是最低的。 具体说C#语言,事件的实现依赖于“代理”(delegate)的概念,先了解一下代理。 代理(delegate) delegate是C#中的一种类型,它实际上是一个能够持有对某个方法的引用的类。与其它的类不同,delegate类能够拥有一个签名(signature),并且它只能持有与它的签名相匹配的方法的引用。它所实现的功能与C/C++中的函数指针十分相似。它允许你传递一个类A的方法m给另一个类B的对象,使得类B的对象能够调用这个方法m。但与函数指针相比,delegate有许多函数指针不具备的优点。首先,函数指针只能指向静态函数,而delegate既可以引用静态函数,又可以引用非静态成员函数。在引用非静态成员函数时,delegate不但保存了对此函数入口指针的引用,而且还保存了调用此函数的类实例的引用。其次,与函数指针相比,delegate是面向对象、类型安全、可靠的受控(managed)对象。也就是说,runtime能够保证delegate指向一个有效的方法,你无须担心delegate会指向无效地址或者越界地址。 实现一个delegate是很简单的,通过以下3个步骤即可实现一个delegate: 1. 声明一个delegate对象,它应当与你想要传递的方法具有相同的参数和返回值类型。 2. 创建delegate对象,并将你想要传递的函数作为参数传入。 3. 在要实现异步调用的地方,通过上一步创建的对象来调用方法。 下面是一个简单的例子: public class MyDelegateTest { // 步骤1,声明delegate对象 public delegate void MyDelegate(string name); // 这是我们欲传递的方法,它与MyDelegate具有相同的参数和返回值类型 public static void MyDelegateFunc(string name) { Console.WriteLine("Hello, {0}", name); } public static void Main () { // 步骤2,创建delegate对象 MyDelegate md = new MyDelegate(MyDelegateTest.MyDelegateFunc); // 步骤3,调用delegate md("sam1111"); } } 输出结果是:Hello, sam1111 下面我们来看看事件是如何处理的: 事件(event) C#中的事件处理实际上是一种具有特殊签名的delegate,象下面这个样子: public delegate void MyEventHandler(object sender, MyEventArgs e); 其中的两个参数,sender代表事件发送者,e是事件参数类。MyEventArg类用来包含与事件相关的数据,所有的事件参数类都必须从System.EventArgs类派生。当然,如果你的事件不含特别的参数,那么可以直接用System.EventArgs类作为参数。 结合delegate的实现,我们可以将自定义事件的实现归结为以下几步: 1:定义对象类型,它有两个参数,第一个参数是事件发送者对象,第二个参数是事件参数类对象。 2:定义事件参数类,此类应当从System.EventArgs类派生。如果事件不带参数,这一步可以省略。 3:定义事件处理方法,它应当与delegate对象具有相同的参数和返回值类型。 4:用event关键字定义事件对象,它同时也是一个delegate对象。 5:用+=操作符添加事件到事件队列中(-=操作符能够将事件从队列中删除)。 6:在需要触发事件的地方用调用delegate的方式写事件触发方法。一般来说,此方法应为protected访问限制,既不能以public方式调用,但可以被子类继承。名字是可以是OnEventName 7:在适当的地方调用事件触发方法触发事件。 下面是一个例子,例子模仿容器和控件的模式,由控件触发一个事件,在容器中捕捉并且进行处理。 事件的触发者: /**//// /// 事件的触发者 /// public class Control { public delegate void SomeHandler(object sender, System.EventArgs e); /**//** * 可以采用系统提供的System.EventHandler, 这里为了说明情况使用了自己定义的delegate * 如果需要在事件的参数中使用自己定义的类型,也要自己定义delegate */ //public event System.EventHandler SomeEvent; public event SomeHandler SomeEvent;   public Control() { //这里使用的delegate必须与事件中声名的一致 //this.SomeEvent += new System.EventHandler(this.Control_SomeEvent); this.SomeEvent += new SomeHandler(this.ProcessSomeEvent); } public void RaiseSomeEvent() { EventArgs e = new EventArgs(); Console.Write("Please input 'a':"); string s = Console.ReadLine(); //在用户输入一个小a的情况下触发事件,否则不触发 if (s == "a") { SomeEvent(this, e); } } //事件的触发者自己对事件进行处理,这个方法的参数必须和代理中声名的一致 private void ProcessSomeEvent(object sender, EventArgs e) { Console.WriteLine("hello"); } } 事件的接收者: /**//// /// 事件的接收和处理者 /// class Container { private Control ctrl = new Control(); public Container() { //这里使用的delegate必须与事件中声名的一致 //ctrl.SomeEvent += new EventHandler(this.OnSomeEvent); ctrl.SomeEvent += new Control.SomeHandler(this.ResponseSomeEvent); ctrl.RaiseSomeEvent(); } public static void Main() { Container pane = new Container(); //这个readline是暂停程序用的,否则画面会一闪而过什么也看不见 Console.ReadLine(); } //这是事件的接受者对事件的响应 private void ResponseSomeEvent(object sender, EventArgs e) { Console.WriteLine("Some event occur!"); } } 程序运行的结果如下: please input 'a':a hello Some event occur!   事件的应用 例如有下面的需求需要实现:程序主画面中弹出一个子窗口。此时主画面仍然可以接收用户的操作(子窗口是非模态的)。子窗口上进行某些操作,根据操作的结果要在主画面上显示不同的数据。我发现一些程序员这样实现这个功能: 主画面弹出子窗口后,将自己的指针交给子画面,然后在子画面中使用这个指针,调用主画面提供的方法,改变主画面上的数据显示。这样虽然可以达到目的,但是各个模块之间产生了很强的耦合。一般说来模块之间的调用应该是单方向的:模块A调用了模块B,模块B就不应该反向调用A,否则就破坏了程序的层次,加强了耦合程度,也使得功能的改变和追加变得很困难。 这时正确的做法应该是在子窗口的操作过程中发出各种事件,而由主窗口捕捉这些事件进行处理,各个模块专心的做自己的事情,不需要过问其他模块的事情。 参考文献 sam111的blog:http://blog.csdn.net/sam1111/ C#中的delegate和event

时间: 2024-10-03 10:09:50

C#中的“代理”和“事件”的相关文章

C#中的代理(delegate)

有些书上也称"代表"或"委托".     C#中取消了指针的概念.对指针恋恋不舍的程序员可以有两种解决方法:声明"非安全"(unsafe)代码段然后在其中使用指针或者使用C#的一个引用类型--"代理"(delegate)."代理"相当与C中的函数指针原型,区别是C#是类型安全的. "代理"(delegate)(代表.委托):"代理"是类型安全的并且完全面向对象的.(1

实例解析C++/CLI之代理与事件

在C++/CLI中,代理是对函数进行包装的对象:而事件是一种为客户程序提供通知的类机制. 在前几篇文章中,已经多次演示了如果让一个句柄在不同的时间,被引用至不同的对象,从而以更抽象的方法来解决程序中的问题,但是,也能使用代理通过函数来达到同样的效果:代理是包装了函数的一个对象,且对实例函数而言,也能通过特定的实例,与这些函数发生联系.一旦一个代理包装了一个或多个函数,你就能通过代理来调用这些函数,而无须事先了解包装了哪些函数. 请看例1中的代码,在标号1中,定义一个代理类型Del,由于使用了上下

手把手带你在 Swift 中应用代理(Delegate)

本文讲的是手把手带你在 Swift 中应用代理(Delegate), 什么是代理呢?在软件开发中,存在许多用于解决特定场景中的普遍问题的通用方案架构,这些所谓的"模板",常被称为设计模式. 代理就是一种设计模式,它允许某个对象在特定事件发生时,向另一个对象发送消息. 试想对象 A 调用对象 B 来执行某项操作.该操作完成后,对象 A 理应感知到对象 B 已经完成了任务以便采取后续的其它必要操作,而代理模式便能帮助我们完成这样的要求! 为了更好地理解这个概念,我将用 Swift 写一个简

用XPath在PHP Web站点中显示Google Calendar事件

简介:Google Calendar 和其他在线日历应用程序将提供简单的集中式系统,在这类系统中,在线社 区可以维护事件日历,而社区成员可以获得关于最新事件的信息.但是许多组织更倾向于在其社区门户. 论坛或博客中显示事件日历.他们经常把事件日历信息从在线日历应用程序复制到 Web 站点中,降低了 在线集中管理事件的效率.Google Calendar 提供了集成应用程序接口 (API),从而为这个问题提供了很 好的解决方案.了解如何使用 XPath 提取并在 PHP Web 站点中显示 Goog

html的DOM中Event对象onblur事件用法实例

 这篇文章主要介绍了html的DOM中Event对象onblur事件用法,实例分析了onblur事件的使用范围与对应的javascript使用技巧,需要的朋友可以参考下     本文实例讲述了html的DOM中Event对象onblur事件用法.分享给大家供大家参考.具体分析如下: onblur 事件会在对象失去焦点时发生. 语法如下: 代码如下: onblur="SomeJavaScriptCode" SomeJavaScriptCode 必需.规定该事件发生时执行的 JavaScr

jquery中交替点击事件的实现代码

 这篇文章主要介绍了jquery中交替点击事件的实现代码,需要的朋友可以参考下  代码如下: $('#clickId').toggle(  function(){$('#divId').hide();},  function(){$('#divId').show();}  );    toggle方法中有两个参数,分别是要交替执行的事件.如果不传参默认是显示隐藏功能   

link中如何判断一个事件里面有几个方法?如何选择性调用?

问题描述 link中如何判断一个事件里面有几个方法?如何选择性调用? link中如何判断一个事件里面有几个方法?如何选择性调用? 解决方案 using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Reflection; using System.Text;

窗体-C#中如何添加FormClose事件 按了关闭,程序没有停止调试,求大神指导

问题描述 C#中如何添加FormClose事件 按了关闭,程序没有停止调试,求大神指导 刚开始学习C#窗体 做登录界面 在登录按钮设置了 连接一个成功提示,但是进去了按关闭的时候,窗体并没有停止运行,一直在调试,求该如何解决,希望大神们帮帮忙 private void button1_Click(object sender, EventArgs e) { if(textBox1.Text=="admin"&&textBox2.Text=="123")

qtcreator-QT中的鼠标移动事件无法检测到

问题描述 QT中的鼠标移动事件无法检测到 自己写的主界面,基类是QFrame,所以所有功能都要自己写,现在想实现的功能是当鼠标在可以伸缩主界面的区域(主界面边框)里时鼠标形状变为示意可以伸缩的形状.但是现实是只有当鼠标移动到相应区域并按下鼠标然后移动,鼠标的形状才会改变,不按下就没有任何反应. 解决方案 http://zhidao.baidu.com/link?url=Ng37doC7FVUqCIk4E7awx2kS5Kk1ObeBzD7CjiifA-G1rd3uPISm1Z47lpYIyOD-