在C#编程语言中,委托、事件和代理是核心特性,它们在构建可扩展和响应式的应用程序时扮演着重要角色。下面将详细解释这些概念。 首先,**委托**(Delegate)是C#中的一个类型,它代表了一种方法调用。你可以将其想象为一个指向方法的指针,允许你在程序的不同部分之间传递方法。委托类型是预先定义的,例如Action和Func系列,或者你可以自定义委托类型。创建委托类型的实例后,你可以将一个或多个方法绑定到这个实例,这称为多播委托。例如,以下是一个自定义委托的例子: ```csharp public delegate int MyDelegate(string s, bool b); ``` 在这个例子中,`MyDelegate`是一个委托类型,它接受一个字符串参数和一个布尔值,返回一个整数。然后,你可以创建这个委托类型的实例,并将符合此签名的方法绑定到它: ```csharp private int SomeFunction(string str, bool bln) { ... } MyDelegate myDelegate = new MyDelegate(SomeFunction); ``` 这里,`myDelegate`可以调用`SomeFunction`,就像调用普通方法一样,但还可以在运行时动态地附加或移除其他方法。 其次,**事件**(Event)是C#中的一种特殊的委托,用于实现发布/订阅模式。事件通常用于当一个对象需要通知其他对象关于某些特定情况的发生时,比如按钮被点击或计时器触发。事件的声明包含两部分:事件声明和事件处理程序。例如: ```csharp public event NumberReachedEventHandler NumberReached; ``` 这里,`NumberReached`是事件,类型为`NumberReachedEventHandler`,它通常是一个自定义的事件处理程序委托,例如: ```csharp public delegate void NumberReachedEventHandler(object sender, NumberReachedEventArgs e); ``` 事件处理程序委托通常具有一个`sender`参数,表示引发事件的对象,以及一个自定义的事件参数类`NumberReachedEventArgs`。 当一个类(如`Counter`)想要发布事件时,它会声明一个事件,并提供一个方法来引发这个事件。例如,当计数达到某个值时,`Counter`可能会引发`NumberReached`事件: ```csharp++ protected virtual void OnNumberReached(NumberReachedEventArgs e) { NumberReached?.Invoke(this, e); } ``` 而其他类(订阅者)可以通过在实例化`Counter`时附加自己的处理程序方法来订阅这个事件: ```csharp counter.NumberReached += HandleNumberReached; ``` 这里的`HandleNumberReached`是符合`NumberReachedEventHandler`签名的方法。 最后,**代理**(Agent)在C#中通常指的是能够代表其他对象执行操作的对象。虽然这个词在C#的官方文档中并不常用,但在某些上下文中,它可以指代实现了某种代理行为的类,例如远程代理(Remote Proxy)或动态代理(Dynamic Proxy),它们允许对对象进行间接访问或拦截方法调用。 委托、事件和代理是C#中实现通信和解耦的关键工具。委托允许你传递方法调用,事件则提供了安全的通知机制,而代理可以用于封装和增强对象的行为。理解和熟练使用这些概念对于编写高质量的C#代码至关重要。
什么是委托?
委托和事件这两个概念是完全配合的。委托仅仅是函数指针,那就是说,它能够引用函数,通过传递地址的机制完成。委托是一个类,当你对它实例化时,要提供一个引用函数,将其作为它构造函数的参数。
每一个委托都有自己的签名,例如:Delegate int SomeDelegate(string s, bool b);是一个委托申明,在这里,提及的签名,就是说SomeDelegate 这个委托 有 string 和 bool 类型的形参,返回一个int 类型。
上面提及的:当你对委托实例化时,要提供一个引用函数,将其作为它构造函数的参数。这里要注意了:被引用的这个函数必须和委托有相同的签名。
看下面的函数:
private int SomeFunction(string str, bool bln){...}
你可以把这个函数传给SomeDelegate的构造函数,因为他们有相似的签名(in other words,他们都有相同的形参类型和个数,并且返回相同的数据类型)。
SomeDelegate sd = new SomeDelegate(SomeFunction);
sd 引用了 SomeFunction,也就是说,SomeFunction已被sd所登记注册,如果你调用 sd,SomeFunction 这个函数也会被调用,记住:我所说 SomeFunction的含义,后面,我们会用到它。
现在,你应该知道如何使用委托了,让我们继续理解事件之旅……
事件的理解
我们知道,在C#中:
?按钮(Button)就是一个类,当我们单击它时,就触发一次click事件。
?时钟(Timer)也是一个类,每过一毫秒,就触发一次tick事件。
让我们通过一个例子来学习,假定有这样的情节:
现在有一个Counter的类,它有一个方法 CountTo(int countTo, int reachableNum),该方法表示:在指定的时间段内(0~~countTo),当到达指定的时间点reachableNum时,就触发一次NumberReached事件。
它还有一个事件:NumberReached,事件是委托类型的变量。意思是:如果给事件命名,用event关键字和要使用的委托类型申明它即可,如下所示:
public event NumberReachedEventHandler NumberReached;
在上面的申明中,NumberReachedEventHandle 仅是一个委托,更确切的表示应该是:NumberReachedDelegate。但是微软从不这样认为MouseDelegate或者PaintDelegate,,而是称谓:MouseEventHandler 或者 PaintEventHandler。所以NumberReachedEventHandler 比NumberReachedDelegate听起来更方便一些,OK?好了,让我们继续,现在你知道了,在我们声明事件之前,需要象下面这样的形式来定义委托:
public delegate void NumberReachedEventHandler(object sender, NumberReachedEventArgs e);
现在声明的委托 NumberReachedEventHandle,它有一个void 返回值,和object,NumberReachedEventArgs两个形参。就像我们在第一节中强调的那样,当实例化委托时,作为实参传入的函数也必须拥有和委托同样的签名。
在你的代码中,你是否用过PaintEventArgs 或者 MouseEventArgs来确定鼠标的移动位置?是否在触发Paint事件的对象中用过Graphics 属性?实际上,为用户提供数据的类都是继承于System.EventArgs类,就是我们常说的事件参数类,如果事件不提供参数,就不定义该类。在我们的例子中,我们通过下面的类提供预期的时间点。
public class NumberReachedEventArgs : EventArgs
{
private int _reached;
public NumberReachedEventArgs(int num)
{
this._reached = num;
public int ReachedNumber
{
get
{
return _reached;
}
}
}
好,有了前面的介绍,让我们到Counter类里面看看:
namespace Events
{
public delegate void NumberReachedEventHandler(object sender,
NumberReachedEventArgs e);
/// <summary>
/// Summary description for Counter.
/// </summary>
public class Counter
{
public event NumberReachedEventHandler NumberReached;
public Counter()
{
//
// TODO: Add constructor logic here
//
}
public void CountTo(int countTo, int reachableNum)
{
if(countTo < reachableNum)
throw new ArgumentException(
剩余12页未读,继续阅读
- 粉丝: 6
- 资源: 10
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
最新资源
- ExtJS 2.0 入门教程与开发指南
- 基于TMS320F2812的能量回馈调速系统设计
- SIP协议详解:RFC3261与即时消息RFC3428
- DM642与CMOS图像传感器接口设计与实现
- Windows Embedded CE6.0安装与开发环境搭建指南
- Eclipse插件开发入门与实践指南
- IEEE 802.16-2004标准详解:固定无线宽带WiMax技术
- AIX平台上的数据库性能优化实战
- ESXi 4.1全面配置教程:从网络到安全与实用工具详解
- VMware ESXi Installable与vCenter Server 4.1 安装步骤详解
- TI MSP430超低功耗单片机选型与应用指南
- DOS环境下的DEBUG调试工具详细指南
- VMware vCenter Converter 4.2 安装与管理实战指南
- HP QTP与QC结合构建业务组件自动化测试框架
- JsEclipse安装配置全攻略
- Daubechies小波构造及MATLAB实现