C#委托用法详解(附带实例)
C# 的委托是一种对方法的引用,类似于 C/C++ 的函数指针(function pointer),但是 C# 的委托与 C 的不同在于其面向对象,安全性较佳。所谓的委托从程序设计端的观点来看,就是将一个方法(method)或函数当作参数,传递给另一个方法。
简单地说,就是一个方法的参数是方法。
过去我们设计方法时,所传递的参数有 int、double、string 等对象。如果设计的方法比较复杂,就会传递多个不同类型的参数,这时在方法内会有 if 的条件判断语句,然后在不同的 if 条件区块内完成这些参数的工作。
上述 [存取修饰词] 是可选项,其他是必需的,下列是声明委托 NumOperation() 的实例:
有的程序设计师会分段撰写,先创建委托对象,再绑定方法实例化,如下:
【实例 1】简单委托的实例,笔者分别使用完整与简化声明委托目标对象,同时调用时也使用 Invoke() 和简化 () 方法。
读者可以留意第 11 行和第 12 行创建委托目标对象的方法:
【实例 2】重新设计实例 1 先声明委托对象,再绑定方法的写法。
【实例 3】使用 MyDelegate 类创建方法,然后让顶级语句设定委托对象目标方法,再予以调用,这个程序多设计了回传静态变量的方法。
【实例 4】将委托指向其他方法。
程序第 5 行将委托指向 SampleMethod 类的静态方法 StaticMethod(),程序第 6 行执行委托 obj(),可以得到第 2 行的输出。
【实例 5】委托当作方法的参数。
【实例 6】使用两个委托,然后用“+”创建多播委托,然后用“-”移除多播委托中的一个委托,分别调用这些委托同时观察执行结果。
【实例 7】声明泛型委托,然后分别使用 int 和 string 数据类型实体化此泛型委托对象。
简单地说,就是一个方法的参数是方法。
过去我们设计方法时,所传递的参数有 int、double、string 等对象。如果设计的方法比较复杂,就会传递多个不同类型的参数,这时在方法内会有 if 的条件判断语句,然后在不同的 if 条件区块内完成这些参数的工作。
C#委托操作
委托操作有 4 个步骤:- 声明委托,这是类型声明,所以顶级语句必须在此委托声明前面;
- 设计目标方法;
- 创建委托对象实例并将此实例绑定目标方法,也可以说是引用方法(reference the method);
- 调用(invoke)委托。
1) 声明委托
声明委托的关键词是 delegate,语法如下:[存取修饰词] delegate 数据类型 委托名称(系列参数);
上述 [存取修饰词] 是可选项,其他是必需的,下列是声明委托 NumOperation() 的实例:
public delegate int NumOperation(int x, int y);注意,委托声明必须在顶级语句后面,在顶级语句设计中来说委托一般都放在程序后方,但是在类声明之前。
2) 设计目标方法
可以创建目标方法,下列是实例:public static int Add(int a, int b) { return a + b; } public static int Sub(int a, int b) { return a - b; }所设计的目标方法与委托必须符合下列条件:
- 有相同的数据类型,此例中 NumOperation() 委托数据类型是 int,Add() 和 Sub() 数据类型也都是 int;
- 目标方法与委托声明有相同的方法签名,也就是参数数目与类型需要相同。Add() 和 Sub() 方法所传递的参数都是 (int, int),这也和 NumOperation() 相同。
3) 创建委托对象实例并设定目标方法的常见用法
我们可以使用 new 关键词创建委托对象,设定目标方法,例如,前面我们创建了两个目标方法,当我们创建委托对象时需要设定此对象的方法,下列是实例:NumOperation opAdd = new NumOpertion(Add); NumOperation opSub = new NumOpertion(Sub);上述 opAdd 的目标方法是 Add,opSub 的目标方法是 Sub。当委托对象 opAdd 经过委托设定目标是 Add 方法后,这个 opAdd 对象就有和 Add 方法相同的能力。
4) 创建委托对象实例并设定目标方法的简化用法
还可以将创建委托对象实例简化如下:NumOperation opAdd = Add; NumOperation opSub = Sub;
有的程序设计师会分段撰写,先创建委托对象,再绑定方法实例化,如下:
NumOperation opAdd, opSub; opAdd = Add; opSub = Sub;
5) 调用委托
可以使用 Invoke() 或是直接使用 () 调用委托,下列是实例:opAdd.Invoke(5, 2);或是:
opAdd(5, 2);
6) 简单的委托实例
因为顶级语句必须在类型声明前方,所以委托是在程序下方声明。【实例 1】简单委托的实例,笔者分别使用完整与简化声明委托目标对象,同时调用时也使用 Invoke() 和简化 () 方法。
static int Add(int a, int b) { return a + b; } static int Sub(int a, int b) { return a - b; } NumOperation opAdd = new NumOperation(Add); // 创建对象与目标是 Add NumOperation opSub = Sub; // 创建对象与目标是 Sub // 使用委托对象调用方法 Console.WriteLine($"Delegate Add 结果:{opAdd.Invoke(5, 2)}"); Console.WriteLine($"Delegate Sub 结果:{opSub(5, 2)}"); delegate int NumOperation(int x, int y); // 声明委托执行结果为:
Delegate Add 结果: 7
Delegate Sub 结果: 3
读者可以留意第 11 行和第 12 行创建委托目标对象的方法:
- 第 11 行 Add 方法是委托 NumOperation() 的参数;
- 第 12 行是简化写法,Sub 方法是委托 NumOpertion() 的参数;
- 另外第 15 行和第 16 行也使用两种方式传递参数调用委托。
【实例 2】重新设计实例 1 先声明委托对象,再绑定方法的写法。
static int Add(int a, int b) { return a + b; } static int Sub(int a, int b) { return a - b; } NumOperation opAdd, opSub; // 创建委托对象 opAdd 和 opSub opAdd = Add; // 设置 opAdd 委托目标方法是 Add opSub = Sub; // 设置 opSub 委托目标方法是 Sub // 使用委托对象调用方法 Console.WriteLine($"Delegate Add 结果:{opAdd.Invoke(5, 2)}"); Console.WriteLine($"Delegate Sub 结果:{opSub(5, 2)}"); delegate int NumOperation(int x, int y); // 声明委托执行结果与实例 1 相同。上个程序的重点是第 11~13 行,在面向对象的概念中,委托的目标方法大多在类内声明,详情可以参考下列实例。
【实例 3】使用 MyDelegate 类创建方法,然后让顶级语句设定委托对象目标方法,再予以调用,这个程序多设计了回传静态变量的方法。
NumOperation opAdd = MyDelegate.Add; // 创建对象与目标是 Add NumOperation opSub = MyDelegate.Sub; // 创建对象与目标是 Sub // 使用委托对象调用方法 opAdd(5, 2); Console.WriteLine($"Delegate Add 结果 {MyDelegate.getNum()}"); opSub(5, 2); Console.WriteLine($"Delegate Sub 结果 {MyDelegate.getNum()}"); delegate int NumOperation(int x, int y); // 声明委托 public class MyDelegate { static int result = 0; // 静态变量 public static int Add(int a, int b) // 加法 { result = a + b; return result; } public static int Sub(int a, int b) // 减法 { result = a - b; return result; } public static int getNum() // 回传静态变量 { return result; } }执行结果与实例 1 相同。
7) 调整委托指向
当委托声明后同时会绑定此委托的目标,也就是将委托目标封装在对象内,但是程序执行过程我们仍是可以将委托指向其他目标方法。【实例 4】将委托指向其他方法。
SampleMethod sm = new SampleMethod(); // 创建类 SampleMethod 对象 MyDelegate obj = sm.InstanceMethod; // 目标是 InstanceMethod 方法的委托 obj(); // 执行 obj() obj = SampleMethod.StaticMethod; // 将委托指向 StaticMethod 方法 obj(); // 执行 obj() delegate void MyDelegate(); // 声明委托 public class SampleMethod { public void InstanceMethod() { Console.WriteLine("我是 InstanceMethod 方法"); } public static void StaticMethod() { Console.WriteLine("我是 StaticMethod 方法"); } }执行结果为:
我是 InstanceMethod 方法
我是 StaticMethod 方法
程序第 5 行将委托指向 SampleMethod 类的静态方法 StaticMethod(),程序第 6 行执行委托 obj(),可以得到第 2 行的输出。
C#委托当作方法的参数
委托也可以被当作方法的参数,详情可以参考下列实例。【实例 5】委托当作方法的参数。
static void InvokeDelegate(MyDelegate delegateObj) { delegateObj("认识 C# delegate 委托对象当作方法的参数"); } MyDelegate objA = JobA.MethodA; // 创建委托当作与目标 InvokeDelegate(objA); // 调用 InvokeDelegate() 方法 MyDelegate objB = JobB.MethodB; // 创建委托当作与目标 InvokeDelegate(objB); // 调用 InvokeDelegate() 方法 public delegate void MyDelegate(string message); // 声明委托 public class JobA // 类 JobA { public static void MethodA(string msg) { Console.WriteLine($"调用 MethodA :{msg}"); } } public class JobB // 类 JobB { public static void MethodB(string msg) { Console.WriteLine($"调用 MethodB :{msg}"); } }执行结果为:
调用 MethodA :认识 C# delegate 委托对象当作方法的参数
调用 MethodB :认识 C# delegate 委托对象当作方法的参数
C#多播委托
委托可以指向多个方法,这个概念称为多播委托(multicast delegate),在这个概念下可以用“+”或是“+=”将方法加到调用列表中,使用“-”或是“-=”则是将特定方法删除。【实例 6】使用两个委托,然后用“+”创建多播委托,然后用“-”移除多播委托中的一个委托,分别调用这些委托同时观察执行结果。
MultiDelegate hiDel, byeDel, multiDel, subHiDel; hiDel = Greeting.Hi; // 设置委托 hiDel 的引用方法为 Hi byeDel = Greeting.Goodbye; // 设置委托 byeDel 的引用方法为 Goodbye // 两个委托相加,创建多播委托(Multicast Delegate) multiDel = hiDel + byeDel; // 从多播委托(Multicast Delegate)中移除 hiDel 委托 subHiDel = multiDel - hiDel; Console.WriteLine("启动 delegate hiDel:"); hiDel("Person A"); Console.WriteLine("启动 delegate byeDel:"); byeDel("Person B"); Console.WriteLine("启动 delegate multiDel:"); multiDel("Person C"); Console.WriteLine("启动 delegate subHiDel:"); subHiDel("Person D"); delegate void MultiDelegate(string s); // 声明委托 public class Greeting { // Define two methods that have the same signature as CustomDel. public static void Hi(string name) // 定义 Hi() 方法 { Console.WriteLine($" 早安, {name}!"); } public static void Goodbye(string name) // 定义 Goodbye() 方法 { Console.WriteLine($" 再见, {name}!"); } }执行结果为:
启动 delegate hiDel:
早安, Person A!
启动 delegate byeDel:
再见, Person B!
启动 delegate multiDel:
早安, Person C!
再见, Person C!
启动 delegate subHiDel:
再见, Person D!
C#泛型委托
在 C# 程序中,也可以将泛型应用到委托中,定义泛型委托(generic delegate),这时设定委托对象的目标方法时需指定数据类型。【实例 7】声明泛型委托,然后分别使用 int 和 string 数据类型实体化此泛型委托对象。
Add<int> sum = MyAdd.Sum; // 声明泛型委托对象和目标方法 Console.WriteLine(sum(3, 6)); Add<string> cat = MyAdd.Concat; // 声明泛型委托对象和目标方法 Console.WriteLine(cat("C# ", "是面向对象程序语言")); public delegate T Add<T>(T x, T y); // 声明泛型委托 Add public class MyAdd { // 参数是 int public static int Sum(int a, int b) { return a + b; } // 参数是 string public static string Concat(string str1, string str2) { return str1 + str2; } }执行结果为:
9
C# 是面向对象程序语言