之前看了一遍委托,但是并不是太理解,今天来整理一下。
委托是一种安全封装方法的类型,有点类似于C和C++中的函数指针,委托是面向对象的、类型安全的。委托的类型由委托的名称定义,下面的实例声明一个委托:
//该委托可以封装一个将字符串作为参数并返回void的方法:Callback
public delegate void CallBack(string message);
//为委托创建一个方法
public static void DelegateMethod(string message)
{
Console.WriteLine(message);
}
//实例化一个委托
CallBack handler = DelegateMethod;
//调用委托
handler("Hello World");
委托类型派生自.NET中的Delegate类。委托类型是密封的(不能从中派生),并且无法从Delegate派生自定义类。由于实例化的委托是一个对象,因此可以将其作为参数传递,也可以将其分配给属性。这允许方法接受委托作为参数,并在以后的某个时间调用委托。这称为异步回调,是在长进程完成时通知调用方的常用方法。以这种方式使用委托时,使用委托的代码不需要了解所使用方法的实现。该功能类似于封装接口提供的功能。
委托对象通常通过提供委托将包装的方法的名称和或使用lambda表达式来构造。以这种方式实例化委托,就可以调用它。调用委托将调用附加到委托实例的方法。调用方传递给委托的参数将传递给该方法,并且该方法的返回值(如果有)将由委托返回给调用方。
回调的另一个常见用途是定义自定义比较方法并将该委托传递给排序方法。它允许调用方的代码称为排序算法的一部分。以下示例方法使用类型作为参数:
public static void MethodWithCallback(int param1,int param2,Callback callback)
{
callback("The number is:" + (param1 + param2).ToString());
}
//可以将上面创建的委托传递给该方法
MethodWithCallback(1,2,handler);
输出:3
使用委托作为抽象,不需要直接调用控制台,也不必在设计时考虑控制台。所做的只是准备一个字符串并将字符串传递给另一个方法。这尤其强大,因为委托方法可以使用任意数量的参数。
当构造委托以包装示例方法时,委托同时引用实例和方法。除了它包装的方法之外,委托不知道实例类型,因此委托可以引用任何类型的对象,只要该对象上有一个与委托签名匹配的方法。当构造委托以包装静态方法时,它仅引用该方法。
调用时,委托可以调用多个方法。这称为多播。要将额外的方法添加到委托的方法列表(调用列表)中,只需要使用加法或加法赋值运算符("+"或"+=")添加两个委托。
public class MethodClass
{
public void Method1(string message){ }
public void Method2(string message){ }
}
//第一种
var obj = new MethodClass();
Callback d1 = obj.Method1;
Callback d2 = obj.Method2;
Callback d3 = DelegateMethod;
//第二种
Callback allMethodsDelegate = d1 + d2;
allMethodsDelegate += d3;
//两种调用方式都有效
多播委托在事件处理中广泛使用。事件源对象将事件通知发送给已注册接受该事件的接收方对象。若要注册事件,收件人需要创建一个旨在处理该事件的方法,然后为该方法创建一个委托,并将该委托传递给事件源。事件发生时,源调用委托。然后,委托在接收者身上调用事件处理方法,传递事件数据。
委托的主要特点包括:
- 类型安全:委托是类型安全的,它们具有特定的签名,只能引用与其签名匹配的方法。
- 封装方法调用:通过委托,可以将一个或多个方法封装到一个变量中,以便在需要时进行调用。
- 多播功能:委托可以引用多个方法,这些方法可以通过
+=
运算符进行组合,形成一个委托链,当委托被调用时,链中的所有方法都会被依次执行。
使用委托可以实现许多功能,例如:
- 回调函数:将委托作为参数传递给其他方法,使得方法可以在适当的时候调用委托所引用的方法。
- 事件处理:在事件触发时,通过委托来调用注册的事件处理程序方法。
- 异步编程:使用委托可以方便地实现异步操作的回调。
注:文中提到的签名解释:
方法的签名包括以下几个方面:
- 方法名:方法的名称。
- 参数列表:方法接受的参数的类型、顺序和数量。
方法签名的作用是确保方法的唯一性,以便在编译和运行时正确地引用和调用方法。不同的方法可以有相同的名称,但它们的参数列表必须不同才能区分开。
void PrintMessage(string message);
void PrintMessage(string message, int times);
这两个方法的名称都是 PrintMessage
,但是它们的参数列表不同,一个接受一个字符串参数,另一个接受一个字符串参数和一个整数参数。因此,它们的签名是不同的,可以通过参数列表的不同来区分它们。