委托delegate是函数指针的升级版

一切皆地址

  • 变量(数据)是以某个地址为起点的一段内存中所存储的值
  • 函数(算法)是以某个地址为起点的一段内存中所存储的一组机器语言命令

直接调用与间接调用

  • 直接调用:通过函数名来调用函数,cpu通过函数名直接获取函数所在的地址并开始执行
  • 间接调用:通过函数指针来调用函数,cpu通过读取函数指针存储的值获得函数所在的地址并开始执行

委托的意思

说白了就是我把方法交给你,让你替我执行,我不执行。
我想杀个人,但是我不想自己动手,委托给小明,让小明帮我杀这个人。最后人被杀了,我还没有亲自动手就完成了我要做的事。

  1. class Test
  2. {
  3. public int Add(int a,int b)
  4. {
  5. return a + b;
  6. }
  7. public int Sub(int a,int b)
  8. {
  9. return a - b;
  10. }
  11. public void Show()
  12. {
  13. Console.WriteLine("打印一段话");
  14. }
  15. }

Action与Func

C#类库自带的委托

  1. static void Main(string[] args)
  2. {
  3. Test test = new Test();
  4. test.Show();//这是直接使用
  5. //传入一个没有返回值的方法名,可以有参数
  6. Action action = new Action(test.Show);
  7. //或者这种简写
  8. Action action1 = test.Show;
  9. action.Invoke();//执行委托
  10. action();//简写
  11. //泛型,类型是参数类型,最后一个是返回值类型
  12. Func<int, int, int> func1 = new Func<int, int, int>(test.Add);
  13. Func<int, int, int> func2 = new Func<int, int, int>(test.Sub);
  14. //简写
  15. Func<int, int, int> func3 = test.Sub;
  16. //执行委托
  17. int x = func1.Invoke(100, 200);
  18. int y = func2(100, 200);
  19. Console.WriteLine("{0},{1}",x,y);
  20. }

delegate委托最基本的使用

自定义委托是一个类

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Test test = new Test();
  6. //实例化委托对象,传入一个返回类型,参数类型和声明委托一样的方法名
  7. Dele dele = new Dele(test.Add);
  8. Dele dele1 = new Dele(test.Sub);
  9. //使用委托
  10. int a = dele.Invoke(100, 200);
  11. int b = dele1(200, 200);
  12. Console.WriteLine(a);
  13. Console.WriteLine(b);
  14. }
  15. }
  16. //声明委托,和类同级
  17. public delegate int Dele(int a, int b);

委托的一般使用

把方法当做参数传给另一个方法使用

模板方法

  1. class Product
  2. {
  3. public string Name { get; set; }
  4. }
  5. class Box
  6. {
  7. public Product Product { get; set; }
  8. }
  9. class WrapFactory
  10. {
  11. //传入一个委托类型,这个委托返回一个Product对象
  12. public Box WrapProduct(Func<Product> getProduct)
  13. {
  14. Box box = new Box();
  15. //执行委托
  16. Product product = getProduct.Invoke();
  17. box.Product = product;//用委托返回的值赋给box对象的Product属性
  18. return box;
  19. }
  20. }
  21. class ProductFactory
  22. {
  23. //制作披萨
  24. public Product MakePizze()
  25. {
  26. Product product = new Product();
  27. product.Name = "披萨";
  28. return product;
  29. }
  30. //制作玩具车
  31. public Product MakeToyCar()
  32. {
  33. Product product = new Product();
  34. product.Name = "玩具车";
  35. return product;
  36. }
  37. }
  1. static void Main(string[] args)
  2. {
  3. ProductFactory productFactory = new ProductFactory();//产品工厂
  4. WrapFactory wrapFactory = new WrapFactory();//包装工厂
  5. //本生产是由工厂来做的,现在工厂不想自己生产,找Func来代工生产
  6. Func<Product> func1 = new Func<Product>(productFactory.MakePizze);//委托制作披萨
  7. Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);//委托制作玩具车
  8. //包装厂需要传入一个代工制作的Func对象
  9. Box box1 = wrapFactory.WrapProduct(func1);
  10. Box box2 = wrapFactory.WrapProduct(func2);
  11. Console.WriteLine(box1.Product.Name);
  12. Console.WriteLine(box2.Product.Name);
  13. }

回调方法callback

  1. //记录程序的运行状态
  2. class Logger
  3. {
  4. public void Log(Product product)
  5. {
  6. Console.WriteLine("产品:{0}在{1}被创建了,价格是{2}",product.Name,DateTime.UtcNow,product.Price);
  7. }
  8. }
  9. class Product
  10. {
  11. public string Name { get; set; }
  12. public int Price { get; set; }
  13. }
  14. class Box
  15. {
  16. public Product Product { get; set; }
  17. }
  18. class WrapFactory
  19. {
  20. //传入一个委托类型,这个委托返回一个Product对象
  21. public Box WrapProduct(Func<Product> getProduct,Action<Product> logCallback)
  22. {
  23. Box box = new Box();
  24. //执行委托
  25. Product product = getProduct.Invoke();
  26. //如果一个产品的价格大于等于50就调用回调函数记录一下
  27. if (product.Price >= 50)
  28. {
  29. logCallback.Invoke(product);
  30. }
  31. box.Product = product;//用委托返回的值赋给box对象的Product属性
  32. return box;
  33. }
  34. }
  35. class ProductFactory
  36. {
  37. //制作披萨
  38. public Product MakePizze()
  39. {
  40. Product product = new Product();
  41. product.Name = "披萨";
  42. product.Price = 12;
  43. return product;
  44. }
  45. //制作玩具车
  46. public Product MakeToyCar()
  47. {
  48. Product product = new Product();
  49. product.Name = "玩具车";
  50. product.Price = 121;
  51. return product;
  52. }
  53. }
  1. static void Main(string[] args)
  2. {
  3. ProductFactory productFactory = new ProductFactory();//产品工厂
  4. WrapFactory wrapFactory = new WrapFactory();//包装工厂
  5. //本生产是由工厂来做的,现在工厂不想自己生产,找Func来代工生产
  6. Func<Product> func1 = new Func<Product>(productFactory.MakePizze);//委托制作披萨
  7. Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);//委托制作玩具车
  8. Logger logger = new Logger();
  9. //声明一个没有返回值的委托,把logger.Log委托给log,要传入的值是Product类型
  10. Action<Product> log = new Action<Product>(logger.Log);
  11. //包装厂需要传入一个代工制作的Func对象,还有回调函数log记录产品信息
  12. Box box1 = wrapFactory.WrapProduct(func1,log);
  13. Box box2 = wrapFactory.WrapProduct(func2,log);
  14. Console.WriteLine(box1.Product.Name);
  15. Console.WriteLine(box2.Product.Name);
  16. }

总结

上面的把委托传入方法中。比如传入A方法的委托到B方法里面。其实就是在B方法里面调用A方法。为什么要用委托?和反射一个道理,要在B方法里面调用的方法一定是高度可定制的,就是说可以是A方法,可以是C方法。X方法。写代码时也不知道需要什么方法啊,不能写死,就让使用这个方法的人传一个方法过来,用什么方法你说了算,你把你要用的方法传给我,我调用就是。但是C#好像不能传递方法类型的参数,所以就发明了个委托,带传方法。

单播委托与多播委托

  1. public class Student
  2. {
  3. public int ID { get; set; }//学生id
  4. public ConsoleColor PneColor { get; set; }//控制台输出的颜色
  5. public Student() { }
  6. public Student(int id,ConsoleColor cc)
  7. {
  8. this.ID = id;
  9. this.PneColor = cc;
  10. }
  11. public void DoHomework()
  12. {
  13. for(int i = 0; i < 2; i++)
  14. {
  15. Console.ForegroundColor = this.PneColor;
  16. Console.WriteLine("学生{0}做作业{1}小时",this.ID,i);
  17. Thread.Sleep(100);
  18. }
  19. }
  20. }
  1. static void Main(string[] args)
  2. {
  3. Student stu1 = new Student(1,ConsoleColor.Yellow);
  4. Student stu2 = new Student(2, ConsoleColor.Green);
  5. Student stu3 = new Student(3, ConsoleColor.Red);
  6. //学生做作业委托给action来做
  7. Action action1 = new Action(stu1.DoHomework);
  8. Action action2 = new Action(stu2.DoHomework);
  9. Action action3 = new Action(stu3.DoHomework);
  10. //一个委托封装一个方法,就是单播委托
  11. action1();
  12. action2();
  13. action3();
  14. //一个委托封装多个方法就是多播委托
  15. action1 += action2 += action3;
  16. action1();
  17. }

image.png

委托隐式异步调用

  1. static void Main(string[] args)
  2. {
  3. Student stu1 = new Student(1,ConsoleColor.Yellow);
  4. Student stu2 = new Student(2, ConsoleColor.Green);
  5. Student stu3 = new Student(3, ConsoleColor.Red);
  6. //学生做作业委托给action来做
  7. Action action1 = new Action(stu1.DoHomework);
  8. Action action2 = new Action(stu2.DoHomework);
  9. Action action3 = new Action(stu3.DoHomework);
  10. //自动生成一个分支线程,第一个参数是回调函数,调用完了这个方法后有什么后续的回调吗,没有就null
  11. //第二个参数一般也是null
  12. action1.BeginInvoke(null,null);
  13. action2.BeginInvoke(null, null);
  14. action3.BeginInvoke(null, null);
  15. }

总结

委托可以用接口代替,面向接口编程