C# 中委托(Delegate)和事件(Event)的概念
- 委托(Delegate):
- 委托是一种类型,它定义了方法的签名,允许将方法作为参数传递给其他方法,也可以将多个方法添加到一个委托实例中,形成方法调用列表。可以把委托看作是一个函数指针的类型安全版本。例如,有一个委托
public delegate void MyDelegate(int num);
,它定义了一个接受一个 int
类型参数且无返回值的方法签名。任何符合这个签名的方法,如 public void PrintNumber(int num) { Console.WriteLine(num); }
都可以添加到这个委托的实例中。
- 事件(Event):
- 事件是一种特殊的委托实例,它基于委托来实现发布 - 订阅模式。事件的发布者(拥有事件的类)可以通知多个订阅者(注册了事件处理方法的对象)发生了某个特定的事情。例如,在一个图形化界面程序中,按钮点击事件就是一个事件,按钮类是发布者,当按钮被点击时,会通知所有注册了该点击事件处理方法的对象进行相应处理。
委托和事件的联系
- 基于委托实现:事件本质上是基于委托实现的。事件实际上是一个受限制的委托实例,它对委托的使用进行了一些封装和约束。
- 订阅机制:都使用类似的订阅机制,即可以向委托或事件中添加方法(订阅),在合适的时机调用这些方法(触发委托或事件)。
委托和事件的区别
- 访问修饰符:
- 委托可以有各种访问修饰符,如
public
、private
等,它可以在程序的任何地方被实例化和调用。
- 事件通常使用
public
修饰符,但它的访问受到限制。事件只能在声明它的类内部触发,外部只能进行订阅和取消订阅操作。例如,在类 A
中声明了一个事件 public event MyDelegate MyEvent;
,在类 A
外部不能直接调用 MyEvent()
来触发事件,只能使用 AInstance.MyEvent += new MyDelegate(SomeMethod);
来订阅事件。
- 功能侧重点:
- 委托更侧重于方法的封装和传递,主要用于将方法作为参数传递给其他方法,或者实现回调机制。比如在
ThreadPool.QueueUserWorkItem(new WaitCallback(MyMethod));
中,WaitCallback
就是一个委托,将 MyMethod
方法传递给线程池的工作项。
- 事件侧重于实现发布 - 订阅模式,用于在对象间进行事件通知。例如,在一个游戏中,角色死亡事件会通知游戏系统进行相关处理,如更新分数、记录日志等。
- 内存管理:
- 委托如果使用不当,可能会导致内存泄漏。例如,当一个委托实例持有对某个对象的强引用,而这个对象本应被释放,但由于委托的引用而无法释放时,就会发生内存泄漏。
- 事件由于其访问限制,在一定程度上减少了这种风险,因为外部不能随意触发事件,只能进行订阅和取消订阅操作,更有利于内存管理。
实际代码中定义和使用委托与事件的示例
- 委托示例:
using System;
// 定义委托
public delegate void MyDelegate(int num);
class Program
{
// 定义符合委托签名的方法
public static void PrintNumber(int num)
{
Console.WriteLine(num);
}
// 方法接受委托作为参数
public static void DoSomething(MyDelegate del, int num)
{
del(num);
}
static void Main()
{
MyDelegate myDel = new MyDelegate(PrintNumber);
DoSomething(myDel, 10);
}
}
- 事件示例:
using System;
// 定义委托
public delegate void MyEventHandler(object sender, EventArgs e);
class Publisher
{
// 定义事件
public event MyEventHandler MyEvent;
public void RaiseEvent()
{
if (MyEvent != null)
{
MyEvent(this, EventArgs.Empty);
}
}
}
class Subscriber
{
public void HandleEvent(object sender, EventArgs e)
{
Console.WriteLine("Event received by subscriber.");
}
}
class Program
{
static void Main()
{
Publisher publisher = new Publisher();
Subscriber subscriber = new Subscriber();
publisher.MyEvent += subscriber.HandleEvent;
publisher.RaiseEvent();
}
}