# 定义
- 委托(delegate)是函数指针的 “升级版”
- 一切皆地址
- 变量(数据)是以某个地址为起点的一段内存中所存储的值
- 函数(算法)是以某个地址为起点的一段内存中所存储的一组机器语言指令
- 直接调用与间接调用
- 直接调用:通过函数名来调用函数,CPU 通过函数名直接获得函数所在地址并开始执行 ➡ 返回
- 间接调用:通过函数指针来调用函数,CPU 通过读取函数指针存储的值获得函数所在地址并开始执行 ➡ 返回
- Java 中没有与委托相对应的功能实体
- 委托的简单使用
# C 语言函数指针
直接调用
| #include <stdio.h> |
| |
| int Add(int a, int b) |
| { |
| int result = a + b; |
| return result; |
| } |
| int Sub(int a, int b) |
| { |
| int result = a - b; |
| return result; |
| } |
| |
| int main() |
| { |
| int x = 100; |
| int y = 200; |
| int z = 0; |
| |
| z = Add(x, y); |
| printf("%d+%d=%d\n", x, y, z); |
| z = Sub(x, y); |
| printf("%d+%d=%d\n", x, y, z); |
| |
| system("pause"); |
| return 0; |
| } |
间接调用:函数指针
| #include <stdio.h> |
| |
| typedef int(* Calc)(int a, int b); |
| |
| int Add(int a, int b) |
| { |
| int result = a + b; |
| return result; |
| } |
| int Sub(int a, int b) |
| { |
| int result = a - b; |
| return result; |
| } |
| |
| int main() |
| { |
| int x = 100; |
| int y = 200; |
| int z = 0; |
| |
| Calc funcPoint1 = &Add; |
| Calc funcPoint2 = ⋐ |
| |
| z = funcPoint1(x, y); |
| printf("%d+%d=%d\n", x, y, z); |
| |
| z = funcPoint2(x, y); |
| printf("%d+%d=%d\n", x, y, z); |
| |
| system("pause"); |
| return 0; |
| } |
# Java
Java 语言由 C++ 发展而来,为了提高应用安全性,Java 语言禁止程序员直接访问内存地址
即 Java 语言把 C++ 中所有与指针相关的内容都舍弃掉了
# 委托实例 Action 与 Func
Action 和 Func 是 C# 内置的委托实例,它们都有很多重载以方便使用
示例:Action
| class Program |
| { |
| static void Main(string[] args) |
| { |
| Calculator calculator = new Calculator(); |
| calculator.Report(); |
| |
| Action action = new Action(calculator.Report); |
| action.Invoke(); |
| action(); |
| } |
| } |
| class Calculator |
| { |
| public void Report() |
| { |
| Console.WriteLine("I have 3 methods."); |
| } |
| |
| public int Add(int a, int b) |
| { |
| return a + b; |
| } |
| |
| public int Sub(int a, int b) |
| { |
| return a - b; |
| } |
| } |
示例:Func
| class Program |
| { |
| static void Main(string[] args) |
| { |
| Calculator calculator = new Calculator(); |
| |
| Func<int, int, int> func1 = new Func<int, int, int>(calculator.Add); |
| Func<int, int, int> func2 = new Func<int, int, int>(calculator.Sub); |
| |
| int x = 100; |
| int y = 200; |
| int z = 0; |
| |
| z = func1.Invoke(x, y); |
| Console.WriteLine(z); |
| z = func2.Invoke(x, y); |
| Console.WriteLine(z); |
| |
| |
| z = func1(x, y); |
| z = func2(x, y); |
| } |
| } |
| class Calculator |
| { |
| public void Report() |
| { |
| Console.WriteLine("I have 3 methods."); |
| } |
| |
| public int Add(int a, int b) |
| { |
| return a + b; |
| } |
| |
| public int Sub(int a, int b) |
| { |
| return a - b; |
| } |
| } |
# 声明(自定义委托)
- 委托是一种类(class),类是数据类型,所以委托也是一种数据类型
- 声明方式与一般的类不同,主要是为了照顾可读性和 C/C++ 传统
- 注意声明委托的位置
- 委托与所封装的方法必须 “类型兼容”
- 返回值的数据类型一致
- 参数列表在个数和数据类型上一致(参数名不需要一样)
委托是类,所以声明位置是和 class 处于同一个级别。但 C# 允许嵌套声明类(一个类里面可以声明另一个类),所以有时也会有 delegate 在 class 内部声明的情况
| static void Main(string[] args) |
| { |
| Type t = typeof(Action); |
| Console.WriteLine(t.IsClass); |
| } |
示例
| public delegate double Calc(double x, double y); |
| |
| class Program |
| { |
| static void Main(string[] args) |
| { |
| Calculator calculator = new Calculator(); |
| Calc calc1 = new Calc(calculator.Mul); |
| |
| Console.WriteLine(calc1(5, 6)); |
| } |
| } |
| |
| class Calculator |
| { |
| public double Mul(double x, double y) |
| { |
| return x * y; |
| } |
| |
| public double Div(double x, double y) |
| { |
| return x / y; |
| } |
| } |
# 一般使用
把方法当作参数传给另一个方法
- 模版方法:“借用” 指定的外部方法来产生结果
- 回调(callback)方法:调用指定的外部方法
⚠️ 注意:难精通 + 易使用 + 功能强大东西,一旦被滥用则后果非常严重
缺点:
- 这是一种方法级别的紧耦合,现实工作中要慎之又慎
- 使可读性下降、debug 的难度增加
- 把委托回调、异步调用和多线程纠缠在一起,会让代码变得难以阅读和维护
- 委托使用不当有可能造成内存泄漏和程序性能下降
# 模板方法
利用模板方法,提高代码复用性
示例
| class Program |
| { |
| static void Main(string[] args) |
| { |
| ProductFactory productFactory = new ProductFactory(); |
| WrapFactory wrapFactory = new WrapFactory(); |
| |
| Func<Product> func1 = new Func<Product>(productFactory.MakePizza); |
| Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar); |
| |
| Box box1 = wrapFactory.WrapProduct(func1); |
| Box box2 = wrapFactory.WrapProduct(func2); |
| |
| Console.WriteLine(box1.Product.Name); |
| Console.WriteLine(box2.Product.Name); |
| } |
| } |
| |
| class Product |
| { |
| public string Name { get; set; } |
| } |
| |
| class Box |
| { |
| public Product Product { get; set; } |
| } |
| |
| class WrapFactory |
| { |
| public Box WrapProduct(Func<Product> getProduct) |
| { |
| Box box = new Box(); |
| Product product = getProduct.Invoke(); |
| box.Product = product; |
| return box; |
| } |
| } |
| |
| class ProductFactory |
| { |
| public Product MakePizza() |
| { |
| Product product = new Product(); |
| product.Name = "Pizza"; |
| return product; |
| } |
| |
| public Product MakeToyCar() |
| { |
| Product product = new Product(); |
| product.Name = "Toy Car"; |
| return product; |
| } |
| } |
| |
| |
| Pizza |
| Toy Car |
# 回调方法
通过委托类型参数传入主调方法的被调用方法,主调方法根据自己的逻辑决定是否调用这个方法
示例
| class Program |
| { |
| static void Main(string[] args) |
| { |
| ProductFactory productFactory = new ProductFactory(); |
| WrapFactory wrapFactory = new WrapFactory(); |
| |
| Func<Product> func1 = new Func<Product>(productFactory.MakePizza); |
| Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar); |
| |
| Logger logger = new Logger(); |
| Action<Product> log = new Action<Product>(logger.Log); |
| Logger logger2 = new Logger(); |
| |
| Box box1 = wrapFactory.WrapProduct(func1, log); |
| Box box2 = wrapFactory.WrapProduct(func2, log); |
| |
| Console.WriteLine(box1.Product.Name); |
| Console.WriteLine(box2.Product.Name); |
| } |
| } |
| |
| |
| class Logger |
| { |
| public void Log(Product product) |
| { |
| |
| Console.WriteLine($"Product {product.Name} created at {DateTime.UtcNow} , Price is {product.Price}"); |
| } |
| } |
| |
| class Product |
| { |
| public string Name { get; set; } |
| public double Price { get; set; } |
| } |
| |
| class Box |
| { |
| public Product Product { get; set; } |
| } |
| |
| class WrapFactory |
| { |
| public Box WrapProduct(Func<Product> getProduct, Action<Product> logCallBack) |
| { |
| Box box = new Box(); |
| Product product = getProduct.Invoke(); |
| if (product.Price >= 50) { |
| logCallBack(product); |
| } |
| |
| box.Product = product; |
| return box; |
| } |
| } |
| |
| class ProductFactory |
| { |
| public Product MakePizza() |
| { |
| Product product = new Product(); |
| product.Name = "Pizza"; |
| product.Price = 12; |
| return product; |
| } |
| |
| public Product MakeToyCar() |
| { |
| Product product = new Product(); |
| product.Name = "Toy Car"; |
| product.Price = 100; |
| return product; |
| } |
| } |
| |
| |
| Product Toy Car created at 2024/11/11 9:00:33 , Price is 100 |
| Pizza |
| Toy Car |
# 注意委托滥用
# 高级使用
# 多播(multicast)委托
多播委托即一个委托内部封装不止一个方法
示例
| using System; |
| using System.Threading; |
| |
| namespace DelegateExample |
| { |
| class Program |
| { |
| static void Main(string[] args) |
| { |
| Student stu1 = new Student { ID = 1, PenColor = ConsoleColor.Yellow }; |
| Student stu2 = new Student { ID = 2, PenColor = ConsoleColor.Green }; |
| Student stu3 = new Student { ID = 3, PenColor = ConsoleColor.Red }; |
| Action action1 = new Action(stu1.DoHomeWork); |
| Action action2 = new Action(stu2.DoHomeWork); |
| Action action3 = new Action(stu3.DoHomeWork); |
| |
| |
| |
| |
| |
| |
| |
| action1 += action2; |
| action1 += action3; |
| action1.Invoke(); |
| } |
| } |
| class Student |
| { |
| public int ID { get; set; } |
| public ConsoleColor PenColor { get; set; } |
| public void DoHomeWork() |
| { |
| for (int i = 0; i < 5; i++) { |
| Console.ForegroundColor = PenColor; |
| Console.WriteLine($"Student {ID} doing homework {i} hour(s)"); |
| Thread.Sleep(1000); |
| } |
| } |
| } |
| } |
# 隐式异步调用
# 三种同步调用
示例
| using System; |
| using System.Threading; |
| |
| namespace DelegateExample |
| { |
| class Program |
| { |
| static void Main(string[] args) |
| { |
| Student stu1 = new Student { ID = 1, PenColor = ConsoleColor.Yellow }; |
| Student stu2 = new Student { ID = 2, PenColor = ConsoleColor.Green }; |
| Student stu3 = new Student { ID = 3, PenColor = ConsoleColor.Red }; |
| |
| |
| |
| |
| |
| |
| Action action1 = new Action(stu1.DoHomework); |
| Action action2 = new Action(stu2.DoHomework); |
| Action action3 = new Action(stu3.DoHomework); |
| |
| |
| |
| |
| |
| |
| |
| action1 += action2; |
| action1 += action3; |
| action1.Invoke(); |
| |
| |
| for (var i = 0; i < 10; i++) |
| { |
| Console.ForegroundColor = ConsoleColor.Cyan; |
| Console.WriteLine($"Main thread {i}"); |
| Thread.Sleep(1000); |
| } |
| } |
| } |
| |
| class Student |
| { |
| public int ID { get; set; } |
| public ConsoleColor PenColor { get; set; } |
| |
| public void DoHomework() |
| { |
| for (int i = 0; i < 5; i++) |
| { |
| Console.ForegroundColor = PenColor; |
| Console.WriteLine($"Student {ID} doing homework {i} hour(s)"); |
| Thread.Sleep(1000); |
| } |
| } |
| } |
| } |
# 使用委托进行隐式异步调用 BeginInvoke
示例
| using System; |
| using System.Threading; |
| |
| namespace DelegateExample |
| { |
| class Program |
| { |
| static void Main(string[] args) |
| { |
| Student stu1 = new Student { ID = 1, PenColor = ConsoleColor.Yellow }; |
| Student stu2 = new Student { ID = 2, PenColor = ConsoleColor.Green }; |
| Student stu3 = new Student { ID = 3, PenColor = ConsoleColor.Red }; |
| |
| Action action1 = new Action(stu1.DoHomework); |
| Action action2 = new Action(stu2.DoHomework); |
| Action action3 = new Action(stu3.DoHomework); |
| |
| |
| |
| action1.BeginInvoke(null, null); |
| action2.BeginInvoke(null, null); |
| action3.BeginInvoke(null, null); |
| |
| |
| for (var i = 0; i < 10; i++) |
| { |
| Console.ForegroundColor = ConsoleColor.Cyan; |
| Console.WriteLine($"Main thread {i}"); |
| Thread.Sleep(1000); |
| } |
| } |
| } |
| |
| class Student |
| { |
| public int ID { get; set; } |
| public ConsoleColor PenColor { get; set; } |
| |
| public void DoHomework() |
| { |
| for (int i = 0; i < 5; i++) |
| { |
| Console.ForegroundColor = PenColor; |
| Console.WriteLine($"Student {ID} doing homework {i} hour(s)"); |
| Thread.Sleep(1000); |
| } |
| } |
| } |
| } |
# 使用 Thread 与 Task 进行异步调用
示例
| using System; |
| using System.Threading; |
| using System.Threading.Tasks; |
| |
| namespace DelegateExample |
| { |
| class Program |
| { |
| static void Main(string[] args) |
| { |
| Student stu1 = new Student { ID = 1, PenColor = ConsoleColor.Yellow }; |
| Student stu2 = new Student { ID = 2, PenColor = ConsoleColor.Green }; |
| Student stu3 = new Student { ID = 3, PenColor = ConsoleColor.Red }; |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| Task task1 = new Task(new Action(stu1.DoHomework)); |
| Task task2 = new Task(new Action(stu2.DoHomework)); |
| Task task3 = new Task(new Action(stu3.DoHomework)); |
| task1.Start(); |
| task2.Start(); |
| task3.Start(); |
| |
| |
| for (var i = 0; i < 10; i++) |
| { |
| Console.ForegroundColor = ConsoleColor.Cyan; |
| Console.WriteLine($"Main thread {i}"); |
| Thread.Sleep(1000); |
| } |
| } |
| } |
| |
| class Student |
| { |
| public int ID { get; set; } |
| public ConsoleColor PenColor { get; set; } |
| |
| public void DoHomework() |
| { |
| for (int i = 0; i < 5; i++) |
| { |
| Console.ForegroundColor = PenColor; |
| Console.WriteLine($"Student {ID} doing homework {i} hour(s)"); |
| Thread.Sleep(1000); |
| } |
| } |
| } |
| } |
# 适时地使用接口(interface)取代委托
示例
| using System; |
| |
| namespace DelegateExample |
| { |
| class Program |
| { |
| static void Main(string[] args) |
| { |
| IProductFactory pizzaFactory = new PizzaFactory(); |
| IProductFactory toyCarFactory = new ToyCarFactory(); |
| var wrapFactory = new WrapFactory(); |
| |
| Box box1 = wrapFactory.WrapProduct(pizzaFactory); |
| Box box2 = wrapFactory.WrapProduct(toyCarFactory); |
| |
| Console.WriteLine(box1.Product.Name); |
| Console.WriteLine(box2.Product.Name); |
| } |
| } |
| |
| interface IProductFactory |
| { |
| Product Make(); |
| } |
| |
| class PizzaFactory : IProductFactory |
| { |
| public Product Make() |
| { |
| var product = new Product(); |
| product.Name = "Pizza"; |
| return product; |
| } |
| } |
| |
| class ToyCarFactory : IProductFactory |
| { |
| public Product Make() |
| { |
| var product = new Product(); |
| product.Name = "Toy Car"; |
| return product; |
| } |
| } |
| |
| class Product |
| { |
| public string Name { get; set; } |
| } |
| |
| class Box |
| { |
| public Product Product { get; set; } |
| } |
| |
| class WrapFactory |
| { |
| |
| public Box WrapProduct(IProductFactory productFactory) |
| { |
| var box = new Box(); |
| Product product = productFactory.Make(); |
| box.Product = product; |
| return box; |
| } |
| } |
| } |