委托、Lambda表达式、事件系列04,委托链是怎样形成的, 多播委托, 调用委托链方法,委托链异常处理...

灰太狼 2022-03-29 07:30 236阅读 0赞

委托是多播委托,我们可以通过”+=”把多个方法赋给委托变量,这样就形成了一个委托链。本篇的话题包括:委托链是怎样形成的,如何调用委托链方法,以及委托链异常处理。

□ 调用返回类型为void的委托所形成的委托链方法
□ 调用返回类型不是void的委托所形成的委托链方法
□ 调用返回类型不是void的泛型委托所形成的委托链方法
□ 调用Func泛型委托所形成的委托链方法
□ 调用Action泛型委托所形成的委托链方法
□ 处理委托链异常

调用返回类型为void的委托所形成的委托链方法

来看下面的例子:

  1. namespace ConsoleApplication3
  2. {
  3. internal delegate void MySayDel(string msg);
  4. class Program
  5. {
  6. static void Main(string[] args)
  7. {
  8. MySayDel del = SayHello;
  9. del = (MySayDel)Delegate.Combine(del, new MySayDel(SayNice)); //等同于:del += SayNice;
  10. del += SayOk;
  11. del("darren");
  12. }
  13. static void SayHello(string msg)
  14. {
  15. Console.WriteLine("hello " + msg);
  16. }
  17. static void SayNice(string msg)
  18. {
  19. Console.WriteLine("nice " + msg);
  20. }
  21. static void SayOk(string msg)
  22. {
  23. Console.WriteLine("ok " + msg);
  24. }
  25. }
  26. }

3

最后,调用委托执行方法,最先注册的方法最先执行。”+=”是一种”语法糖”,内部其实调用了Delegate的静态方法Combine,形成委托链,再把委托链赋给委托变量。大致如下:

→当执行MySayDel del = SayHello;
4
→当执行del = (MySayDel)Delegate.Combine(del, new MySayDel(SayNice)),在托管堆上又创建MySayDel委托实例指向SayNice方法,接着复制原先的、指向SayHello方法的委托实例,2个委托实例形成委托链,即蓝色区域部分,栈上的委托变量del指向委托链。
5

调用返回类型不是void的委托所形成的委托链方法

以上,委托的返回类型是void,当调用委托的时候,依次执行委托链的方法。可是,如果委托的返回类型不是void,会不会依次执行委托链的方法呢?

  1. internal delegate int MyCalulateDel(int val1, int val2);
  2. class Program
  3. {
  4. static void Main(string[] args)
  5. {
  6. MyCalulateDel del = Add;
  7. del += Sub;
  8. Console.WriteLine(del.Invoke(20, 10));
  9. }
  10. static int Add(int val1, int val2)
  11. {
  12. return val1 + val2;
  13. }
  14. static int Sub(int val1, int val2)
  15. {
  16. return val1 - val2;
  17. }
  18. }

6
以上,当调用委托不会依次执行委托链方法,而是会执行最后注册的方法。

如果我们想得到所有委托方法的返回结果,该如何做到呢?
--委托为我们提供了一个GetInvocationList的实例方法,可以获取所有委托。

  1. internal delegate int MyCalulateDel(int val1, int val2);
  2. class Program
  3. {
  4. static void Main(string[] args)
  5. {
  6. MyCalulateDel del = Add;
  7. del += Sub;
  8. var result = GetResultForEachDel(del, 20, 10);
  9. foreach (var item in result)
  10. {
  11. Console.WriteLine(item);
  12. }
  13. }
  14. static List<int> GetResultForEachDel(MyCalulateDel del, int val1, int val2)
  15. {
  16. List<int> result = new List<int>();
  17. foreach (MyCalulateDel item in del.GetInvocationList())
  18. {
  19. result.Add(item.Invoke(val1, val2));
  20. }
  21. return result;
  22. }
  23. static int Add(int val1, int val2)
  24. {
  25. return val1 + val2;
  26. }
  27. static int Sub(int val1, int val2)
  28. {
  29. return val1 - val2;
  30. }
  31. }

7
以上,通过GetInvocationList实例方法获取所有的委托,然后分别调用所有的委托方法。

调用返回类型不是void的泛型委托所形成的委托链方法

以上委托只针对int类型,如果不想把类型”写死”,就应该使用泛型委托。

  1. namespace ConsoleApplication5
  2. {
  3. internal delegate T MyGenericDel<T>();
  4. class Program
  5. {
  6. static void Main(string[] args)
  7. {
  8. MyGenericDel<int> d = ReturnOne;
  9. d += ReturnTwo;
  10. var result = GetReturnValues(d);
  11. foreach (var item in result)
  12. {
  13. Console.WriteLine(item);
  14. }
  15. }
  16. //执行所有的泛型委托
  17. static IEnumerable<TModel> GetReturnValues<TModel>(MyGenericDel<TModel> d)
  18. {
  19. //遍历委托链
  20. foreach (MyGenericDel<TModel> del in d.GetInvocationList())
  21. {
  22. yield return del.Invoke();
  23. }
  24. }
  25. static int ReturnOne()
  26. {
  27. return 1;
  28. }
  29. static int ReturnTwo()
  30. {
  31. return 2;
  32. }
  33. }
  34. }

泛型委托,一般是在返回类型名称后面、方法名称后面,形参类型名称后面加上占位符

调用Func泛型委托所形成的委托链方法

而实际上,对于泛型委托,.NET为我们准备了Func,它有多个重载方法:
8

最后一个形参是返回类型,其余形参是输入参数。

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Func<int> d = ReturnOne;
  6. d += ReturnTwo;
  7. var result = GetReturnValues(d);
  8. foreach (var item in result)
  9. {
  10. Console.WriteLine(item);
  11. }
  12. }
  13. //执行所有的泛型委托
  14. static IEnumerable<TModel> GetReturnValues<TModel>(Func<TModel> d)
  15. {
  16. //遍历委托链
  17. foreach (Func<TModel> del in d.GetInvocationList())
  18. {
  19. yield return del();
  20. }
  21. }
  22. static int ReturnOne()
  23. {
  24. return 1;
  25. }
  26. static int ReturnTwo()
  27. {
  28. return 2;
  29. }
  30. }

调用Action泛型委托所形成的委托链方法

如果一个泛型委托没有返回值,就可以使用Action,它有多个重载方法:
9
所有的形参都是输入参数,没有返回值。

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Action<string> action = SayOnce;
  6. action += SayTwice;
  7. action.Invoke("darren");
  8. }
  9. static void SayOnce(string str)
  10. {
  11. Console.WriteLine("我只说一次" + str);
  12. }
  13. static void SayTwice(string str)
  14. {
  15. Console.WriteLine("我第一次说" + str);
  16. Console.WriteLine("我第二次说" + str);
  17. }
  18. }

10

处理委托链异常

在委托链中,如果任何一个委托方法抛出异常,如何处理呢?
--需要遍历委托链,让每个委托单独执行,并编写处理异常代码

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Action<string> action = SayOnce;
  6. action += SayTwice;
  7. foreach (Action<string> a in action.GetInvocationList())
  8. {
  9. try
  10. {
  11. a("darren");
  12. }
  13. catch (Exception)
  14. {
  15. Console.WriteLine("有异常");
  16. }
  17. }
  18. }
  19. static void SayOnce(string str)
  20. {
  21. Console.WriteLine("我只说一次" + str);
  22. }
  23. static void SayTwice(string str)
  24. {
  25. Console.WriteLine("我第一次说" + str);
  26. Console.WriteLine("我第二次说" + str);
  27. throw new Exception();
  28. }
  29. }

11

总结:
○ 如果委托的返回类型是void,并且形成委托链,只要调用委托就会依次执行委托链方法。
○ 如果委托的返回类型不是void,并且形成委托链,可以使用委托的GetInvocationList实例方法获取所有委托,然后遍历这些委托依次执行委托方法得到返回类型。
○ 泛型委托优先考虑使用Func和Action,如果有返回类型使用Func,如果返回类型为void使用Action
○ 委托链的异常处理思路是:遍历委托链中的每个委托,针对每个委托编写捕获异常的代码

“委托、Lambda表达式、事件系列”包括:

委托、Lambda表达式、事件系列01,委托是什么,委托的基本用法,委托的Method和Target属性

委托、Lambda表达式、事件系列02,什么时候该用委托

委托、Lambda表达式、事件系列03,从委托到Lamda表达式

#

委托、Lambda表达式、事件系列04,委托链是怎样形成的, 多播委托, 调用委托链方法,委托链异常处理

#

#

委托、Lambda表达式、事件系列05,Action委托与闭包

委托、Lambda表达式、事件系列06,使用Action实现观察者模式,体验委托和事件的区别

委托、Lambda表达式、事件系列07,使用EventHandler委托

#

#

#

#

发表评论

表情:
评论列表 (有 0 条评论,236人围观)

还没有评论,来说两句吧...

相关阅读

    相关 事件委托

    事件委托概念:利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。 首先封装好EventUtil以便兼容IE var EventUtil = {

    相关 事件委托

      最近刷Zepto视频时,听到里面提到了事件委托,就去查了一下,对事件委托有了些了解,特总结一下。 什么是事件委托   事件委托是利用事件冒泡,只指定一个事件处理程序

    相关 事件委托

    事件委托利用了事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。所有用到按钮的事件(多数鼠标事件和键盘事件)都适合采用事件委托技术, 使用事件委托可以节省内存。

    相关 委托Lambda表达式

    \\\对象初始化器与集合初始化器   即通过构造函数实例化对象或集合,并同时赋值。这样的方式被称为初始化器。 Person p=new Person('张三',2