一、什么是委托
委托是C#中类型安全的,可以订阅一个或多个具有相同签名方法的函数指针。
委托可以把函数做为参数传递,其实际意义便是让别人代理你的事情。
委托可以看做是函数的指针,整数可以用整数变量指向它,对象可以用对象变量指向它,
函数也可以用委托变量指向它。我们可以选择将委托类型看做只定义了一个方法的接口,而委托的实例可以看做是实现了那个接口的一个对象。
简单的讲委托可以看作一个特殊的对象,可以将方法当作参数进行传递,保存对函数的引用。可以将委托看成执行方法的一个东西。
二、委托的使用场景
1.事件处理:在 C# 中,事件通常使用委托来实现。您可以创建一个委托,将事件与委托关联,并在代码中处理事件时使用委托。
例如:如果你在Windows窗体应用中放置了一个按钮,用户点击按钮会触发点击事件,这时候就需要用委托事先定义好触发事件的处理方法
2.回调方法:委托可以用作回调方法,允许您在一个方法执行完毕后调用另一个方法。
例如:当你启用Task异步处理一个tcp下载任务,这时候你需要用委托给他一个下载任务执行完成之后通知任务完成的方法
3.多路广播:使用委托,您可以同时调用多个方法。这种方法称为多路广播,可以在同一事件上广播多个处理程序。
例如:C#的委托可以同时指向多个函数,可以使用MulticastDelegate(多播委托类)
4.集合操作:委托可以用作 LINQ 查询的过滤器,以便在集合中筛选数据。
例如:当你要对某个List集合进行操作时,修改某些数据或者打印值,可以使用:ForEach方法然后将对应的处理方法包装在委托传入
三、委托的使用
委托声明:委托必须先声明再使用
//委托的声明 delegate void MyDel(int value); //委托的是无返回值,且单一int参数的方法 //关键字 返回类型 委托类签名 签名
public delegate void MyNoReturnNoParaDelegate ();//无参数无返回值委托 public delegate void MyNoReturnWithParaDelegate (int x, int y);//带参无返回值委托 public delegate int MyWithReturnWithParaDelegate (int x, int y);//带参有返回值委托
委托实例化:
//实例化一个委托并且将相同前面的异步方法传入 AsyncMethodCaller caller = new AsyncMethodCaller(TestMethodAsync); /// <summary> /// 与委托对应的方法 /// </summary> /// <param name="callDuration"></param> /// <param name="threadId"></param> /// <returns></returns> static string TestMethodAsync(int callDuration, out int threadId) { Stopwatch sw = new Stopwatch(); sw.Start(); Console.WriteLine("异步TestMethodAsync开始"); for (int i = 0; i < 5; i++) { // 模拟耗时操作 Thread.Sleep(callDuration); Console.WriteLine("TestMethodAsync:" + i.ToString()); } sw.Stop(); threadId = Thread.CurrentThread.ManagedThreadId; return string.Format("耗时{0}ms.", sw.ElapsedMilliseconds.ToString()); }
//使用lambda表达式传入一个匿名方法实例化委托 AsyncMethodCaller22 caller2 = new AsyncMethodCaller22((callDuration) => { Stopwatch sw = new Stopwatch(); sw.Start(); Console.WriteLine("异步TestMethodAsync开始"); for (int i = 0; i < 5; i++) { // 模拟耗时操作 Thread.Sleep(callDuration); Console.WriteLine("TestMethodAsync:" + i.ToString()); } sw.Stop(); return string.Format("耗时{0}ms.", sw.ElapsedMilliseconds.ToString()); });
//多播委托的实现 internal class Program { //委托的声明 delegate string MyDel(int value); //委托的是无返回值,且单一int参数的方法 //关键字 返回类型 委托类签名 签名 static void Main(string[] args) { Console.WriteLine("Hello, World!"); MyDel mydel = new MyDel(TestMethodAsync1);//实例化委托,并传入第一个方法 mydel += TestMethodAsync2;//添加第二个方法 mydel.Invoke(1000);//执行 Console.WriteLine("多播委托会按照添加顺序执行传入的方法"); mydel -= TestMethodAsync2;//从委托实例中注销第二个方法 Console.WriteLine("某个方法注销后便不再执行"); mydel.Invoke(1000); Console.ReadKey(); } /// <summary> /// 与委托对应的方法1 /// </summary> /// <param name="callDuration"></param> /// <param name="threadId"></param> /// <returns></returns> static string TestMethodAsync1(int callDuration) { Stopwatch sw = new Stopwatch(); sw.Start(); Console.WriteLine("异步TestMethodAsync1开始"); for (int i = 0; i < 5; i++) { // 模拟耗时操作 Thread.Sleep(callDuration); Console.WriteLine("TestMethodAsync1:" + i.ToString()); } sw.Stop(); return string.Format("耗时{0}ms.", sw.ElapsedMilliseconds.ToString()); } /// <summary> /// 与委托对应的方法2 /// </summary> /// <param name="callDuration"></param> /// <param name="threadId"></param> /// <returns></returns> static string TestMethodAsync2(int callDuration) { Stopwatch sw = new Stopwatch(); sw.Start(); Console.WriteLine("异步TestMethodAsync2开始"); for (int i = 0; i < 5; i++) { // 模拟耗时操作 Thread.Sleep(callDuration); Console.WriteLine("TestMethodAsync2:" + i.ToString()); } sw.Stop(); return string.Format("耗时{0}ms.", sw.ElapsedMilliseconds.ToString()); } }
四、官方提供的两种委托Action与Func
Action:没有返回值,参数可有可无,最多支持16个泛型参数,使用方法与自定义委托类类似
Action action = new Action(NoReturnNoParaMethod); action.Invoke();
Func: 必须有返回值,参数可有可无,最多支持16个泛型参数,使用方法与自定义委托类类似
Func<int, int, int> func1 = new Func<int, int, int>(WithReturnWithParaMethod); int sum = myWithReturnWithParaDelegate(1, 2);