1. 基本了解

1.1 委托简述

官方文档

委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用,用于将方法作为参数传递给其他方法,可将任何可访问类或结构中与委托类型匹配的任何方法分配给委托

其它说明

委托在IL中就是一个类(本质上是类),继承与System.MulticastDelegate类(特殊类)

委托是一个类,它定义了方法的类型,使得可以将方法当作另一个方法的参数来进行传递

委托类型

自定义委托:Delegate

系统内置委托:ActionFunc

委托声明

可以声明在类的外部也可以在内部,在IL中,无论在内外都会编译到类的内部

委托在实例化时,需要传入一个方法,此方法返回值,参数(类型,个数,顺序)与委托一致

1.2 使用步骤

  • 声明一个委托
  • 委托的实例化,传入指定方法
  • 调用执行

2. Delegate委托

Delegate:常用到的一种声明,且至少0个参数,至多32个参数,可以无返回值,也可以指定返回值类型

2.1 示例一:无参,无返回值

  1. // 1.声明委托
  2. public delegate void NoReturnNoPare();
  3. // 2.准备委托执行方法
  4. public void Show()
  5. {
  6. Console.WriteLine("无参,无返回值");
  7. }
  8. // 3.实例,调用委托
  9. public void Start()
  10. {
  11. NoReturnNoPare d1 = new NoReturnNoPare(this.Show);
  12. d1.Invoke();
  13. }

2.2 示例二:有参,无返回值

  1. // 1.声明委托
  2. public delegate void NoReturnWithPare(int x, int y);
  3. // 2.准备委托执行方法
  4. public void Show(int x,int y)
  5. {
  6. Console.WriteLine("有参,无返回值");
  7. }
  8. // 3.实例,调用委托
  9. public void Start()
  10. {
  11. NoReturnWithPare d2 = new NoReturnWithPare(this.Show);
  12. d2.Invoke(1,2);
  13. }

2.3 示例三:有参,有返回值

  1. // 1.声明委托
  2. public delegate int WithReturnWithPare(int x, int y);
  3. // 2.准备委托执行方法
  4. public int Show(int x, int y)
  5. {
  6. return x + y;
  7. }
  8. // 3.实例,调用委托
  9. public void Start()
  10. {
  11. WithReturnWithPare d2 = new WithReturnWithPare(this.Show);
  12. // 返回值类型,编译器会自动推断
  13. int IResult = d2.Invoke(1, 2);
  14. Console.WriteLine(IResult);
  15. }

3. Action委托

Action是系统内置委托(无需声明),是无返回值的泛型委托,至少0个参数,至多16个参数,且无返回值

3.1 示例一:无参,无返回值

  1. // 1.定义执行方法
  2. public void Show()
  3. {
  4. Console.WriteLine("无参,无返回值");
  5. }
  6. // 2.调用执行
  7. public void Start()
  8. {
  9. Action action = new Action(this.Show);
  10. // Action<int, int> action = this.Show;
  11. action.Invoke();
  12. }

3.2 示例二:有参,无返回值

  1. // 1.定义执行方法
  2. public void Show(int x, int y)
  3. {
  4. Console.WriteLine("有参,无返回值");
  5. }
  6. // 2.调用执行
  7. public void Start()
  8. {
  9. Action<int, int> action = new Action<int,int>(this.Show);
  10. // Action<int, int> action = this.Show;
  11. action.Invoke(1,2);
  12. }

3.3 示例三:使用 lambda 表达式

  1. Action<int, int> action = (x, y) => { };
  2. action.Invoke(1, 2);

3.4 示例四:将委托作为方法参数

  1. public void Start()
  2. {
  3. Action<int> action = (x) => { Console.WriteLine(x); };
  4. Show(action, 2);
  5. }
  6. public void Show<T>(Action<T> ac, T inputParam)
  7. {
  8. ac(inputParam);
  9. }

4. Func委托

Func是有返回值的泛型委托,至少0个参数,至多16个参数,根据返回值泛型返回;必须有返回值,不可void,且最后一位泛型类型,为返回值类型

4.1 示例一:无参,有返回值

  1. public void Start()
  2. {
  3. Func<string> func = new Func<string>(this.Show);
  4. string IResult = func.Invoke();
  5. Console.WriteLine(IResult);
  6. }
  7. public string Show()
  8. {
  9. return "libai";
  10. }

4.2 示例二:有参,有返回值

  1. public void Start()
  2. {
  3. Func<int, string> func = new Func<int, string>(this.Show);
  4. string IResult = func.Invoke(1);
  5. Console.WriteLine(IResult);
  6. }
  7. public string Show(int i)
  8. {
  9. return "libai\t" + i;
  10. }

4.3 示例三:使用lambda表达式

  1. public void Start()
  2. {
  3. Func<int> func1 = () => { return 1; };
  4. int IResultInt = func1.Invoke();
  5. Console.WriteLine(IResultInt);
  6. Func<int, string> func2 = (i) => { return i.ToString(); };
  7. string IResult = func2.Invoke(1);
  8. Console.WriteLine(IResult);
  9. }

4.4 示例四:将委托作为方法参数

例子一:简化

  1. public void Start()
  2. {
  3. Func<int, int, int> func = (x, y) => { return x + y; };
  4. int IResultInt = Test(func, 1, 2);
  5. Console.WriteLine(IResultInt);
  6. }
  7. public int Test<T1, T2>(Func<T1, T2, int> func, T1 a, T2 b)
  8. {
  9. return func(a, b);
  10. }

示例二:一般写法

  1. static void Main(string[] args)
  2. {
  3. Console.WriteLine(Test<int,int>(Fun,100,200));
  4. Console.ReadKey();
  5. }
  6. public static int Test<T1, T2>(Func<T1, T2, int> func, T1 a, T2 b)
  7. {
  8. return func(a, b);
  9. }
  10. private static int Fun(int a, int b)
  11. {
  12. return a + b;
  13. }

5. 链式委托

5.1 文档说明

官方文档

委托对象的一个有用属性在于可通过使用 + 运算符将多个对象分配到一个委托实例,多播委托包含已分配委托列表,此多播委托被调用时会依次调用列表中的委托;仅可合并类型相同的委托

  • - 运算符可用于从多播委托中删除组件委托,顺序,从下至上(委托列表中没有移除的委托时不会报错)
  • + 运算符可用于将委托组件添加到委托列表,顺序,从上而下

在执行有返回值的委托链时,只能得到最后一个委托的结果

其它文档

委托链(多播委托)是一个由委托组成的链表,而不是一个新的东西,所有的自定义委托都直接集成自System.MulticastDelegate类型,这个类型即是为委托链而设计的

链式委托是指一个委托的链表,而不是指另外一类特殊的委托,当执行链上的一个方法时,后续委托将会被依此执行

System.MuticastDelegate定义了对链式委托的支持,在System.Delegate的基础上,增加了一个指向后续委托的指针,这样就实现了一个简单的链表结构

5.2 示例一:统一执行

  1. public void Start()
  2. {
  3. NoReturnWithPare noReturnWith = new NoReturnWithPare(this.Fun1);
  4. noReturnWith += this.Fun2;
  5. noReturnWith += this.Fun1;
  6. noReturnWith.Invoke(1, 2);
  7. }
  8. public void Fun1(int x, int y)
  9. {
  10. Console.WriteLine("Fun1:\t" + x + y);
  11. }
  12. public void Fun2(int x, int y)
  13. {
  14. Console.WriteLine("Fun2:\t" + x + y);
  15. }

5.3 示例二:逐个执行

注意:逐个执行时,单项不能用var声明,必须使用委托的具体类型

逐个指定,返回值,问题,b委托使用a的返回值?

  1. public void Start()
  2. {
  3. NoReturnWithPare noReturnWith = new NoReturnWithPare(this.Fun1);
  4. noReturnWith += this.Fun2;
  5. noReturnWith += this.Fun1;
  6. foreach (NoReturnWithPare item in noReturnWith.GetInvocationList())
  7. {
  8. item.Invoke(1,2);
  9. }
  10. }
  11. public void Fun1(int x, int y)
  12. {
  13. Console.WriteLine("Fun1:\t" + x + y);
  14. }
  15. public void Fun2(int x, int y)
  16. {
  17. Console.WriteLine("Fun2:\t" + x + y);
  18. }

5.4 示例三:责任链模式

典型案例:用代码模拟,猫叫了,狗叫了,然后老鼠跑了

普通实现

  1. using System;
  2. namespace de2
  3. {
  4. class Program
  5. {
  6. static void Main(string[] args)
  7. {
  8. Cat cat = new Cat();
  9. cat.Miao();
  10. }
  11. }
  12. public class Cat
  13. {
  14. public void Miao()
  15. {
  16. Console.WriteLine("猫叫了");
  17. new Dog().Wang();
  18. new Mouse().Run();
  19. }
  20. }
  21. public class Dog
  22. {
  23. public void Wang()
  24. {
  25. Console.WriteLine("狗叫了");
  26. }
  27. }
  28. public class Mouse
  29. {
  30. public void Run()
  31. {
  32. Console.WriteLine("老鼠跑了");
  33. }
  34. }
  35. }

使用责任链模式实现(委托)

  1. using System;
  2. namespace de2
  3. {
  4. class Program
  5. {
  6. static void Main(string[] args)
  7. {
  8. Cat cat = new Cat();
  9. cat.miaoAction += new Dog().Wang;
  10. cat.miaoAction += new Mouse().Run;
  11. cat.MiaoDelegate();
  12. }
  13. }
  14. public class Cat
  15. {
  16. public void Miao()
  17. {
  18. Console.WriteLine("猫叫了");
  19. }
  20. public Action miaoAction;
  21. public void MiaoDelegate()
  22. {
  23. this.Miao();
  24. this.miaoAction.Invoke();
  25. }
  26. }
  27. public class Dog
  28. {
  29. public void Wang()
  30. {
  31. Console.WriteLine("狗叫了");
  32. }
  33. }
  34. public class Mouse
  35. {
  36. public void Run()
  37. {
  38. Console.WriteLine("老鼠跑了");
  39. }
  40. }
  41. }

使用责任链模式实现(抽象方法)

  1. using System;
  2. using System.Collections.Generic;
  3. namespace de2
  4. {
  5. class Program
  6. {
  7. static void Main(string[] args)
  8. {
  9. Cat cat = new Cat();
  10. cat.Add(new Dog());
  11. cat.Add(new Mouse());
  12. cat.AbsServer();
  13. }
  14. }
  15. public interface IAbsServer
  16. {
  17. void Do();
  18. }
  19. public class Cat : IAbsServer
  20. {
  21. private List<IAbsServer> list = new List<IAbsServer>();
  22. public void Add(IAbsServer absServer)
  23. {
  24. list.Add(absServer);
  25. }
  26. public void AbsServer()
  27. {
  28. this.Do();
  29. foreach (var item in list)
  30. {
  31. item.Do();
  32. }
  33. }
  34. public void Miao()
  35. {
  36. Console.WriteLine("猫叫了");
  37. }
  38. public void Do()
  39. {
  40. this.Miao();
  41. }
  42. }
  43. public class Dog : IAbsServer
  44. {
  45. public void Do()
  46. {
  47. this.Wang();
  48. }
  49. public void Wang()
  50. {
  51. Console.WriteLine("狗叫了");
  52. }
  53. }
  54. public class Mouse : IAbsServer
  55. {
  56. public void Do()
  57. {
  58. this.Run();
  59. }
  60. public void Run()
  61. {
  62. Console.WriteLine("老鼠跑了");
  63. }
  64. }
  65. }

使用责任链模式实现(事件)

  1. using System;
  2. namespace de2
  3. {
  4. class Program
  5. {
  6. static void Main(string[] args)
  7. {
  8. Cat cat = new Cat();
  9. cat.miaoEvent += new Dog().Wang;
  10. cat.miaoEvent += new Mouse().Run;
  11. cat.MiaoEvent();
  12. }
  13. }
  14. public class Cat
  15. {
  16. public void Miao()
  17. {
  18. Console.WriteLine("猫叫了");
  19. }
  20. /// <summary>
  21. /// 事件,只能在事件所在类(本身类,子类不可)的内部 Invoke 执行
  22. /// </summary>
  23. public event Action miaoEvent;
  24. public void MiaoEvent()
  25. {
  26. this.Miao();
  27. this.miaoEvent.Invoke();
  28. }
  29. }
  30. public class Dog
  31. {
  32. public void Wang()
  33. {
  34. Console.WriteLine("狗叫了");
  35. }
  36. }
  37. public class Mouse
  38. {
  39. public void Run()
  40. {
  41. Console.WriteLine("老鼠跑了");
  42. }
  43. }
  44. }

5.5 补充说明

链式委托的执行顺序是:按照委托链上的顺醋从当前委托开始依次往后执行,如果有需要可以使用GetInvocationList()方法来获得委托链上所有需要执行的委托,并且按照任何希望的顺序去逐个执行(Invoke

委托可以是带有返回值的方法,但多余一个带返回值的方法被添加到委托链中时,程序员需要手动地调用委托链上的每个方法,否则委托使用者智能得到委托链上最后一个被执行的方法的返回值

委托的应用场合通常是任务的执行者把细节工作进行再分配,执行者确切地知道什么工作将要被执行,但却把执行细节委托给其他组件、方法或者程序集

6. 委托事件

事件(Event):是委托的实例,在定义委托是加了enevt 关键字

enevt 关键字,限定权限,只能在事件所在类中调用事件

6.1 示例一:自定义标准事件

模拟:用户订阅手机降价事件,当降价时用于购买手机

  1. using System;
  2. namespace de3
  3. {
  4. class Program
  5. {
  6. static void Main(string[] args)
  7. {
  8. Phone phone = new Phone
  9. {
  10. name = "vivo",
  11. Price = 1999
  12. };
  13. phone.DiscountEventHandler += new User() { name = "李白" }.Buy;
  14. phone.Price -= 400;
  15. }
  16. }
  17. // 事件额外信息
  18. public class EventPara
  19. {
  20. public int oValue { get; set; }
  21. public int nValue { get; set; }
  22. }
  23. public delegate void CostomEventHandler(object sender, EventPara para);
  24. // 手机,发布者,发布事件并且在满足条件情况下执行事件
  25. public class Phone
  26. {
  27. public string name { get; set; }
  28. private int price;
  29. public int Price
  30. {
  31. set
  32. {
  33. if (value < this.price)
  34. {
  35. this.DiscountEventHandler?.Invoke(this, new EventPara
  36. {
  37. oValue = this.price,
  38. nValue = value
  39. });
  40. }
  41. this.price = value;
  42. }
  43. get { return this.price; }
  44. }
  45. public event CostomEventHandler DiscountEventHandler;
  46. }
  47. // 订户,关注事件,事件发生后执行动作
  48. public class User
  49. {
  50. public string name { get; set; }
  51. // 买手机
  52. public void Buy(object sender, EventPara para)
  53. {
  54. Phone phone = (Phone)sender;
  55. Console.WriteLine($"手机:{phone.name}\t打折前:{para.oValue}\t打折后:{para.nValue}");
  56. Console.WriteLine("购买手机!");
  57. }
  58. }
  59. }

标注:委托事件实际应用还不太熟,示例做参考即可

7. 扩展补充

7.1 委托的内部结构

IL语言的无参无返回值的委托结构(编译后)

  1. .class nested public auto ansi sealed NoReturnNoPare
  2. extends [mscorlib]System.MulticastDelegate
  3. {
  4. // Methods
  5. .method public hidebysig specialname rtspecialname
  6. instance void .ctor (
  7. object 'object',
  8. native int 'method'
  9. ) runtime managed
  10. {
  11. } // end of method NoReturnNoPare::.ctor
  12. .method public hidebysig newslot virtual
  13. instance void Invoke () runtime managed
  14. {
  15. } // end of method NoReturnNoPare::Invoke
  16. .method public hidebysig newslot virtual
  17. instance class [mscorlib]System.IAsyncResult BeginInvoke (
  18. class [mscorlib]System.AsyncCallback callback,
  19. object 'object'
  20. ) runtime managed
  21. {
  22. } // end of method NoReturnNoPare::BeginInvoke
  23. .method public hidebysig newslot virtual
  24. instance void EndInvoke (
  25. class [mscorlib]System.IAsyncResult result
  26. ) runtime managed
  27. {
  28. } // end of method NoReturnNoPare::EndInvoke
  29. } // end of class NoReturnNoPare

7.2 调用委托

使用委托实例调用,参数写在括号中

  1. NoReturnNoPare d1 = new NoReturnNoPare(this.Show);
  2. d1();

使用实例的Invoke()方法调用,参数写在方法中

  1. NoReturnNoPare d1 = new NoReturnNoPare(this.Show);
  2. d1.Invoke();

7.3 Predicate<T>委托

说明:不常用,仅作为了解(看个人情况)

Predicate是返回bool型的泛型委托,至少1个参数,至多1个参数,返回值固定为bool

官方示例

  1. using System;
  2. using System.Drawing;
  3. public class Example
  4. {
  5. public static void Main()
  6. {
  7. Point[] points = { new Point(100, 200),
  8. new Point(150, 250), new Point(250, 375),
  9. new Point(275, 395), new Point(295, 450) };
  10. Predicate<Point> predicate = FindPoints;
  11. Point first = Array.Find(points, predicate);
  12. Console.WriteLine("Found: X = {0}, Y = {1}", first.X, first.Y);
  13. }
  14. private static bool FindPoints(Point obj)
  15. {
  16. return obj.X * obj.Y > 100000;
  17. }
  18. }

04.委托Delegation的更多相关文章

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

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

  2. 大话设计模式--委托--IOS

    最近看了一些关于IOS委托的文章,看完之后,感觉不大好. 引文: 委托delegation是一种简单但是功能强大的设计模式,它的功能是程序中一个对象代表另一个对象,或者一个对象与另外一个对象协同工作. ...

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

    谈到事件注册,EventHandler是最常用的. EventHandler是一个委托,接收2个形参.sender是指事件的发起者,e代表事件参数. □ 使用EventHandler实现猜拳游戏 使用 ...

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

    在"实现观察者模式(Observer Pattern)的2种方式"中,曾经通过接口的方式.委托与事件的方式实现过观察者模式.本篇体验使用Action实现此模式,并从中体验委托与事件 ...

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

    来看使用Action委托的一个实例: static void Main(string[] args) { int i = 0; Action a = () => i++; a(); a(); C ...

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

    在"委托.Lambda表达式.事件系列02,什么时候该用委托"一文中,使用委托让代码简洁了不少. namespace ConsoleApplication2 { internal ...

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

    假设要找出整型集合中小于5的数. static void Main(string[] args) { IEnumerable<int> source = new List<int&g ...

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

    委托是一个类. namespace ConsoleApplication1 { internal delegate void MyDelegate(int val); class Program { ...

  9. Kotlin枚举与委托深入详解

    枚举: 基本上跟Java的差不多,这里就过一遍既可,如下: 还可以接收参数,如下: 枚举还可以定义方法,如下: 看下错误提示: 所以可以这样: 然后咱们再冒号之前定义对象,如下: 下面来使用一下: 当 ...

随机推荐

  1. 整合Spring Cloud Stream Binder与GCP Pubsub进行消息发送与接收

    我最新最全的文章都在南瓜慢说 www.pkslow.com,欢迎大家来喝茶! 1 前言 之前的文章<整合Spring Cloud Stream Binder与RabbitMQ进行消息发送与接收& ...

  2. excel函数提取身份证出生日期,分离日期时间的日期和时间

    1.提取身份证出生日期 =1*TEXT(MID(H13,7,8),"0-00-00")用MID函数提取表示日期的位数,再用text函数转换为格式1998-6-21格式的文本,再通过 ...

  3. Redis之阻塞分析

    Redis是典型的单线程架构,所有的读写操作都是在一条主线程中完成的.当Redis用于高并发场景时,这条线程就变成了它的生命线.如果出现阻塞,哪怕是很短时间,对于我们的应用来说都是噩梦.导致阻塞问题的 ...

  4. AnyCast技术

    在公司项目经历过DDoS攻击后,选用了一些比较成熟的DDoS防护厂商,在学习过程中,发现,许多DDoS厂商的防护技术都离不开 Anycast网络. 所以在这里整理一下AnyCast的相关资料. 1. ...

  5. 分布式AKF拆分原则

    1. 前言 当我们需要分布式系统提供更强的性能时,该怎样扩展系统呢?什么时候该加机器?什么时候该重构代码?扩容时,究竟该选择哈希算法还是最小连接数算法,才能有效提升性能? 在面对 Scalabilit ...

  6. Linux中Crontab的用法

    1.crontab的概念: crontab命令用于设置周期性被执行的指令.该命令从标准输入设备读取指令,并将其存放于"crontab"文件中,以供之后读取和执行.可以使用它在每天的 ...

  7. PowerMock 支持gRPC的Mock Server实现

    PowerMock是一个Mock Server的实现,它同时支持HTTP与gRPC协议接口的Mock,并提供了灵活的插件功能. 这个工具面向于前后端.测试等对有接口Mock需求的开发人员,也可以作为一 ...

  8. Swoole异步投递task任务

    [使用场景] Swoole的task模块可以用来做一些异步的慢速任务.耗时场景.如webim中发广播,发送邮件等,把这些任务丢给task进程之后,worker进程可以继续处理新的数据请求,任务完成后会 ...

  9. C语言:extern应用

    前面我们都是将所有的代码写到一个源文件里面,对于小程序,代码不过几百行,这或许无可厚非,但当程序膨胀代码到几千行甚至上万行后,就应该考虑将代码分散到多个文件中,否则代码的阅读和维护将成为一件痛苦的事情 ...

  10. python -- 正则表达式&re模块(转载)

    1. 正则表达式基础 1.1. 简单介绍 正则表达式并不是Python的一部分.正则表达式是用于处理字符串的强大工具,拥有自己独特的语法以及一个独立的处理引擎,效率上可能不如str自带的方法,但功能十 ...