C# 知识回顾 - 委托 delegate
【博主】反骨仔 【原文】http://www.cnblogs.com/liqingwen/p/6031892.html
目录
What's 委托
delegate 一种自定义的引用类型,它包含了特定的参数列表和返回类型。
使用委托时,只需要对应的方法的签名和返回类型兼容即可,无论是实例方法,抑或是静态方法。通过调用委托的实例就相当于调用方法本身,因为委托存储的是一个方法列表,调用委托的实例就相当于依次调用方法列表的内容。委托它将方法作为参数进行传递给了其它方法,我们常用的事件处理程序就是通过委托调用的方法,也是一种观察者模式的体现。
下面的示例演示了一个委托声明:
public delegate int Del(int x, int y);
使用委托的要求是:方法签名与返回类型兼容。可以是静态方法,也可以是实例方法。
委托的特点
-
类型安全,类似于 C 和 C++ 中的函数指针。
-
可将方法作为参数进行传递。
-
可用于定义回调方法。
-
委托可以链接在一起;例如,可以对一个事件调用多个方法。
-
方法不必与委托类型完全匹配。
使用委托
委托,一种类型,它是安全的,自定义的,委托的名称就决定了这个委托是什么类型。
//该委托可以封装 “,参数类型 string,返回类型 void” 的方法 public delegate void MyDel(string message);
委托的实例对象通常使用两种方式进行构建,直接使用类的方法名,或者使用 Lambda 表达式,当然匿名方法也可以。
在调用委托的时刻,我们将传递到委托的参数会继续传递到委托列表的方法中。如果委托列表中包含返回值的话,会将最后一个返回值返回给调用方。也就是该委托对象调用完毕的返回值。
1 //该委托名为 MyDel,可以封装 “参数类型 string,返回值类型 void” 的方法 2 public delegate void MyDel(string message); 3 4 class Program 5 { 6 static void Main(string[] args) 7 { 8 //实例化委托 9 MyDel del = Print; 10 //调用委托 11 del("Hi"); 12 13 Console.Read(); 14 } 15 16 /// <summary> 17 /// 打印文本 18 /// </summary> 19 /// <remarks>这是一个可用于 MyDel 委托的方法</remarks> 20 /// <param name="message"></param> 21 private static void Print(string message) 22 { 23 Console.WriteLine(message); 24 } 25 }
委托的关键字是 delegate,它派生自 Delegate 类,也是 sealed,即密封类,不能作为基类再继续派生。
异步回调:允许以方法的形式作为参数形式进行传递,并在稍后进行该委托的调用。通过这个形式使用的委托,调用方不需要知道方法的具体实现,只是简单的把它当做一个功能即可,这类似接口的封装。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 MyDel del = Print; 6 CallbackMethod(100, 150, del); //将委托传递到 CallbackMethod 方法 7 8 Console.Read(); 9 } 10 11 /// <summary> 12 /// 回调方法 13 /// </summary> 14 /// <param name="m"></param> 15 /// <param name="n"></param> 16 /// <param name="del"></param> 17 private static void CallbackMethod(int m, int n, MyDel del) 18 { 19 del((m + n).ToString()); 20 } 21 22 private static void Print(string message) 23 { 24 Console.WriteLine(message); 25 } 26 }
在这里的 CallbackMethod 作用是,调用委托,因为它包含的是 Print() 方法的调用,所以只需要传递对应的 string 类型作为参数即可。
我们在创建委托的时候,你可以选择使用的是实例方法或者是静态方法。当你使用的是实例方法时,该委托对象会同时引用该实例的对象及它的方法。委托并不关心应用引用对象的类型,它关心的是,方法签名和返回值兼容,即可。不过,如果你创建委托对象包含的是静态方法的时候,它是只引用该方法的。
使用 += 可以把多个方法添加到一个委托对象的调用列表中,调用一次委托,相当于一次性调用一堆方法。
1 //该委托可以封装 “名 MyDel,参数类型 string,返回值类型 void” 的方法 2 public delegate void MyDel(string message); 3 4 class MyClass 5 { 6 public void Print1(string message) 7 { 8 Console.WriteLine($"{message} - {nameof(Print1)}"); 9 } 10 11 public void Print2(string message) 12 { 13 Console.WriteLine($"{message} - {nameof(Print2)}"); 14 } 15 } 16 17 class Program 18 { 19 static void Main(string[] args) 20 { 21 var myClass = new MyClass(); 22 MyDel del1 = myClass.Print1; 23 MyDel del2 = myClass.Print2; 24 MyDel del3 = Print; 25 26 var del = del1 + del2; 27 del += del3; //这里使用 += 28 del("Hi!"); 29 30 Console.Read(); 31 } 32 33 private static void Print(string message) 34 { 35 Console.WriteLine($"{message} - {nameof(Print)}"); 36 } 37 }
委托对象 del,他内部存储的是一个包含三个方法的调用列表(Print1、Print2 和 Print),在你调用 del 对象时,调用列表中的方法会依次调用。
多播委托:一个委托对象调用多个方法,使用 +=。
若要从委托对象的调用列表中移除方法,需要使用 -=。
1 static void Main(string[] args) 2 { 3 var myClass = new MyClass(); 4 MyDel del1 = myClass.Print1; 5 MyDel del2 = myClass.Print2; 6 MyDel del3 = Print; 7 8 var del = del1 + del2; 9 del += del3; //使用 += 10 del("Hi!"); 11 12 Console.WriteLine("======分割线======"); 13 14 del -= del2; //使用 -= 15 del("Hi!"); 16 17 Console.Read(); 18 }
你也可以编写一些方法获取调用列表中方法的数量:
1 static void Main(string[] args) 2 { 3 var myClass = new MyClass(); 4 MyDel del1 = myClass.Print1; 5 MyDel del2 = myClass.Print2; 6 MyDel del3 = Print; 7 8 var del = del1 + del2; 9 del += del3; //使用 += 10 //del("Hi!"); 11 12 var count = del.GetInvocationList().Length; //获取委托调用列表中方法的数量 13 Console.WriteLine(count); 14 15 Console.WriteLine("======分割线======"); 16 17 del -= del2; //使用 -= 18 //del("Hi!"); 19 20 count = del.GetInvocationList().Length; //获取委托调用列表中方法的数量 21 Console.WriteLine(count); 22 23 Console.Read(); 24 }
多播委托派生自 MulticastDelegate,也是继承自 Delegate的,常用于事件处理中。
传送门
【参考】https://msdn.microsoft.com/zh-cn/library/windows/apps/ms173171(v=vs.120).aspx
【参考】微软官方文档