5.1 委托(Delegates)
> 1 委托是C#中非常强大的特性,类似于C++中的函数指针,但更加类型安全和灵活。委托可以引用一个方法,或者一组方法,当调用委托时,它会执行引用的方法。委托特别适合用于实现回调机制和事件驱动编程。
> 2 想象一下,你是一位活动策划师,你需要组织一场大型活动。在活动中,有很多不同的任务需要完成,比如安排音乐、布置场地、准备食物等等。你可以自己去做所有的事情,但更好的方法是让专业人士来帮你完成这些任务。
委托在编程中就像是这些专业人士。你可以把一个任务委托给一个方法,当需要完成这个任务时,你只需要调用这个委托,而不需要知道具体是哪个方法在执行任务。委托是一种类型安全的函数指针,可以指向一个或多个方法,并在需要时调用它们。
5.1.1 委托的定义和使用
(1) 定义委托:public delegate void MyDelegate(string message);
(2) 声明和实例化委托:
> (1) MyDelegate del = new MyDelegate(MethodB);
> (2) MyDelegate del = MethodB;
以上两种实例方式的效果和意义是一样的,都是将 MethodB 分配给一个新的 MyDelegate 类型的委托实例。不同的是,第一种方式显式地使用了 new 关键字来创建委托实例,而第二种方式是语法糖,编译器会自动将其转换为第一种方式。
(3) 使用委托:del("Hello, World!");
5.1.2 示例
// 定义一个委托类型,名为 MyDelegate,接受一个字符串参数并返回 void
public delegate void MyDelegate(string message);
class Program
{
static void Main()
{
// 声明和实例化委托,并将 MethodA 分配给它
MyDelegate del = MethodA;
// 调用委托,实际调用 MethodA 方法
del("Hello from MethodA");
// 将委托指向 MethodB
del = MethodB;
// 调用委托,实际调用 MethodB 方法
del("Hello from MethodB");
}
// 符合委托签名的方法 MethodA
static void MethodA(string message)
{
Console.WriteLine(message);
}
// 符合委托签名的方法 MethodB
static void MethodB(string message)
{
Console.WriteLine(message);
}
在这个示例中,MyDelegate是一个委托类型,它可以引用任何接受一个字符串参数并返回void的方法。MethodA和MethodB是两个符合该签名的方法,我们可以将它们分配给del委托并调用它们。
5.1.3 拓展
(1)自定义委托
// 定义一个自定义委托类型
public delegate int AddDelegate(int x, int y);
class Program
{
static void Main()
{
// 使用自定义委托类型
AddDelegate add = (x, y) => x + y;
Console.WriteLine(add(2, 3)); // 输出 5
}
}
> 相等于 ---》 (2) Func 委托
class Program
{
static void Main()
{
// 使用 Func 委托
Func<int, int, int> add = (x, y) => x + y;
Console.WriteLine(add(2, 3)); // 输出 5
}
}
> 详细解释
. Func<int, int, int>:
.. 第一个 int:表示方法的第一个参数的类型。
... 第二个 int:表示方法的第二个参数的类型。
.... 第三个 int:表示方法的返回值的类型。
5.1.4 多播委托
多播委托是 C# 中的一种特殊委托类型,它允许一个委托实例包含对多个方法的引用。当调用该委托时,它会依次调用所有被引用的方法。多播委托在事件处理和回调机制中非常有用。
(1) 特点
. 多个方法调用:多播委托可以包含对多个方法的引用,当调用该委托时,会按顺序调用所有方法。
.. 顺序执行:多播委托中的方法按添加顺序执行。
... 返回值处理:如果委托有返回值,多播委托只返回最后一个方法的返回值。
(2) 创建多播委托
可以使用 += 操作符将多个方法附加到一个委托实例上,使用 -= 操作符从委托实例中移除方法。
(3) 定义和使用多播委托
using System;
// 定义一个委托类型,接受一个字符串参数并返回 void
public delegate void MyDelegate(string message);
class Program
{
static void Main()
{
// 声明一个 MyDelegate 类型的委托实例,并将 MethodA 分配给它
MyDelegate del = MethodA;
// 使用 += 操作符将 MethodB 附加到委托实例 del 上
del += MethodB;
// 调用委托实例 del,将依次调用 MethodA 和 MethodB,传递参数 "Hello from Delegate"
del("Hello from Delegate"); //MethodA和MethodB都输出
// 使用 -= 操作符从委托实例 del 上移除 MethodA
del -= MethodA;
// 再次调用委托实例 del,这次只会调用 MethodB,传递参数 "Hello again"
del("Hello again"); //只输出MethodB
}
// 定义符合委托签名的第一个方法 MethodA
static void MethodA(string message)
{
Console.WriteLine("MethodA: " + message);
}
// 定义符合委托签名的第二个方法 MethodB
static void MethodB(string message)
{
Console.WriteLine("MethodB: " + message);
}
}
(4) 处理返回值的多播委托
using System;
using System.Collections.Generic;
// 定义一个返回整数的委托类型,接受一个整数参数
public delegate int MyDelegate(int value);
class Program
{
static void Main()
{
// 声明一个 MyDelegate 类型的委托实例,并将 MethodA 和 MethodB 分配给它
MyDelegate del = MethodA;
del += MethodB;
// 调用委托并获取所有返回值
List<int> results = InvokeDelegate(del, 5);
// 输出所有返回值
Console.WriteLine("Results:");
foreach (int result in results)
{
Console.WriteLine(result);
}
}
// 定义符合委托签名的第一个方法 MethodA
static int MethodA(int value)
{
return value + 1; // 将输入值加 1
}
// 定义符合委托签名的第二个方法 MethodB
static int MethodB(int value)
{
return value + 2; // 将输入值加 2
}
// 定义一个方法来调用多播委托并收集所有返回值
static List<int> InvokeDelegate(MyDelegate del, int value)
{
List<int> results = new List<int>();
// 使用 GetInvocationList 获取委托的调用列表
foreach (MyDelegate handler in del.GetInvocationList())
{
// 调用每个方法,并将返回值添加到 results 列表中
int result = handler(value);
results.Add(result);
}
return results;
}
}
5.2 事件(Events)
事件是基于委托的一种特殊成员,主要用于在一个对象希望通知另一个对象某些事情发生时使用。事件是一种发布-订阅模式的实现,通常用于UI编程和处理用户交互。
5.2.1 定义和使用事件
(1) 定义事件:
public event MyDelegate MyEvent;
(2) 触发事件:
protected virtual void OnMyEvent(string message)
{
if (MyEvent != null) MyEvent(message);
}
(3) 订阅事件:
obj.MyEvent += new MyDelegate(EventHandler);
5.1.2 示例
public delegate void MyDelegate(string message);
class Publisher
{
public event MyDelegate MyEvent;
public void RaiseEvent(string message)
{
if (MyEvent != null) MyEvent(message);
}
}
class Subscriber
{
public void EventHandler(string message)
{
Console.WriteLine("Event received: " + message);
}
}
class Program
{
static void Main()
{
Publisher publisher = new Publisher();
Subscriber subscriber = new Subscriber();
// 订阅事件
publisher.MyEvent += subscriber.EventHandler;
// 触发事件
publisher.RaiseEvent("Hello, Event!");
}
}
在这个示例中,Publisher类定义了一个事件MyEvent,当事件触发时会调用所有订阅的处理程序。Subscriber类有一个EventHandler方法,它将被注册为事件处理程序。Publisher对象触发事件时,Subscriber对象的处理程序被调用。
> 委托是对方法的引用,可以存储和调用方法。适用于实现回调机制。
> 事件是基于委托的,用于实现发布-订阅模式,适用于通知其他对象某些事情发生。