type
status
date
slug
summary
tags
category
icon
password
Property
Aug 14, 2023 11:52 AM

本节内容

什么是委托
委托的声明(自定义委托)
委托的使用

什么是委托

委托(delegate)是函数指针的“升级版”
  • 实例:C/C++中的函数指针
一切皆地址
  • 变量(数据)是以某个地址为起点的一段内存中所存储的值
  • 函数(算法)事以某个地址为起点的一段内存中所存储的一组机器语言指令
直接调用与间接调用
  • 直接调用:通过函数名来调用函数,CPU通过函数名直接获得函数所在地址开始执行→返回
  • 间接调用:通过函数指针来调用函数,CPU通过读取函数指针存储的值获得函数所在地址并开始执行→返回
Java中没有与委托相对应的功能实体
委托的简单使用
  • Action委托
  • Func委托
程序=数据+算法
namespace ConsoleApp4 { internal class Program { static void Main(string[] args) { Calculator calculator = new Calculator(); // Action 委托 Action action = new Action(calculator.Report); calculator.Report();// 直接 action.Invoke();//间接 action();//间接 // Func委托 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);//func1(x, y) Console.WriteLine(z); z = func2.Invoke(x, y);//func2(x, y) Console.WriteLine(z); } } class Calculator { public void Report() { Console.WriteLine("I have 3 methods"); } public int Add(int a,int b) { int result = a + b; return result; } public int Sub(int a, int b) { int result = a - b; return result; } } }

委托的声明(自定义委托)

委托是一种类(class),类是数据类型,所以委托也是一种数据类型
它的声明方式与一般的类不同,主要是为了照顾可读性和C/C++传统
鼠疫声明委托的位置
  • 避免写错地方结果声明成嵌套类型
委托与所封装的方法必须“类型兼容”
notion image
  • 返回值的数据类型一致
  • 参数列表在数个和数据类型上一致(参数名不需要一样)
委托需要生命在名称空间体里面
namespace ConsoleApp4 { public delegate double Calc(double x, double y); internal class Program { static void Main(string[] args) { Type x = typeof(Action); Console.WriteLine(x.IsClass); Calculator calculator = new Calculator(); Calc calc1 = new Calc(calculator.Add); Calc calc2 = new Calc(calculator.Sub); Calc calc3 = new Calc(calculator.Mul); Calc calc4 = new Calc(calculator.Dvi); double a = 100; double b = 200; Console.WriteLine(calc1.Invoke(a, b)); Console.WriteLine(calc2.Invoke(a, b)); Console.WriteLine(calc3.Invoke(a, b)); Console.WriteLine(calc4.Invoke(a, b)); Console.WriteLine(calc1(a, b)); Console.WriteLine(calc2(a, b)); Console.WriteLine(calc3(a, b)); Console.WriteLine(calc4(a, b)); } } class Calculator { public double Add(double a, double b) { double result = a + b; return result; } public double Sub(double a, double b) { double result = a - b; return result; } public double Mul(double a, double b) { double result = a * b; return result; } public double Dvi(double a, double b) { double result = a / b; return result; } } }

委托的一般使用

实例:把方法当做参数传给另一个方法
  • 正确使用1:模板方法,“借用”制定的外部方法来产生结果
    • 相当于“填空题”
    • 常位于代码中部
    • 委托有返回值
  • 正确使用2:回调(callback)方法,调用指定的外部方法
    • 相当于流水线
    • 常位于代码末尾
    • 委托无返回值
注意:难精通+易使用+功能强大的东西,一旦被滥用则会后果非常严重
  • 缺点1:这是一种方法级别的紧耦合,在工作中要慎之又慎
  • 缺点2:使可读性下降,debug难度增加
  • 缺点3:把委托回调,异步调用和多线程纠缠在一起,会让代码变得难以阅读和维护
  • 缺点4:委托使用不当可能造成内存泄漏和程序性能下降
良好的复用结构是所有优秀软件所追求的共同目标之一
模板方法
回调方法
没有返回值的方法,使用Action委托
namespace ConsoleApp4 { public delegate double Calc(double x, double y); internal 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); 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 {0} created at {1}.Price is {2}",product.Name,DateTime.UtcNow, 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(); 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 = "ToyCar"; product.Price = 120; return product; } } }

委托的高级使用

多播(multicast)委托
隐式异步调用
  • 同步与异步的简介
    • 中英文的语言差异
    • 同步:你做完了我(在你的基础上)接着做
    • 异步:咱们两个同时做(相当于汉语中的同步进行)
  • 同步调用与异步调用的对比
    • 每一个运行的程序是一个进程(process)
    • 每个进程可以有一个或多个线程(thread)
    • 同步调用是在同一个线程内
    • 异步调用的底层机理是多线程
    • 穿行==同步==单线程,并行==异步==多线程
  • 隐式多线程vs显示多线程
    • 直接同步调用:使用方法名
    • 间接同步调用:使用单播、多播委托的invoke方法
    • 隐式异步调用:使用委托的BeginInvoke
    • 现实异步调用:使用Thread或Task
应该适时地使用接口(interface)取代一些堆委托的使用
  • Java完全地使用接口取代了委托的功能,即java没有与C#中委托相对应的功能实体
notion image
using System; using System.Collections.Generic; using System.Linq; using System.Threading; namespace ConsoleApp4 { public delegate double Calc(double x, double y); internal 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.Invoke(); action2.Invoke(); action3.Invoke(); */ // 多播 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 = this.PenColor; Console.WriteLine("Student {0} doing homework {1} hour(S)",ID,i); Thread.Sleep(1000); } } } }
同步1
直接调用是同步的
namespace ConsoleApp4 { public delegate double Calc(double x, double y); internal 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 }; stu1.DoHomework(); stu2.DoHomework(); stu3.DoHomework(); for (int i = 0; i < 10; i++) { Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("Main thread {0}",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 = this.PenColor; Console.WriteLine("Student {0} doing homework {1} hour(S)",ID,i); Thread.Sleep(1000); } } } }
间接调用也是同步的
单播也是,多播也是
namespace ConsoleApp4 { public delegate double Calc(double x, double y); internal 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(); /* action1.Invoke(); action2.Invoke(); action3.Invoke();*/ for (int i = 0; i < 10; i++) { Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("Main thread {0}",i); Thread.Sleep(100); } } } class Student { public int ID { get; set; } public ConsoleColor PenColor { get; set; } public void DoHomework() { for (int i = 0; i < 5; i++) { Console.ForegroundColor = this.PenColor; Console.WriteLine("Student {0} doing homework {1} hour(S)",ID,i); Thread.Sleep(100); } } } }
委托的隐式异步调用
beginInvoke方式
namespace ConsoleApp4 { public delegate double Calc(double x, double y); internal 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 (int i = 0; i < 10; i++) { Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("Main thread {0}",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 = this.PenColor; Console.WriteLine("Student {0} doing homework {1} hour(S)",ID,i); Thread.Sleep(1000); } } } }
结果
notion image
 
打印的颜色有的一样,这就是争抢前景色的资源,发生冲突
显示的异步调用,手动声明多线程
一种为使用thread
namespace ConsoleApp4 { public delegate double Calc(double x, double y); internal 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 }; Thread thread1 = new Thread(new ThreadStart(stu1.DoHomework)); Thread thread2 = new Thread(new ThreadStart(stu2.DoHomework)); Thread thread3 = new Thread(new ThreadStart(stu3.DoHomework)); thread1.Start(); thread2.Start(); thread3.Start(); for (int i = 0; i < 10; i++) { Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("Main thread {0}",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 = this.PenColor; Console.WriteLine("Student {0} doing homework {1} hour(S)",ID,i); Thread.Sleep(1000); } } } }
结果
notion image
 
另一种为使用Task的方式
namespace ConsoleApp4 { public delegate double Calc(double x, double y); internal 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 (int i = 0; i < 10; i++) { Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("Main thread {0}",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 = this.PenColor; Console.WriteLine("Student {0} doing homework {1} hour(S)",ID,i); Thread.Sleep(1000); } } } }
结果
notion image
 
使用接口取代委托
重构基本不改变原来的代码,只是把原来的代码放到更合适的地方去
namespace ConsoleApp4 { public delegate double Calc(double x, double y); internal class Program { static void Main(string[] args) { IProductFactory pizzaFactory=new PizzaFactory(); IProductFactory toyCarFactory=new ToyCarFactory(); WrapFactory 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() { Product product = new Product(); product.Name = "Pizza"; product.Price = 12; return product; } } class ToyCarFactory : IProductFactory { public Product Make() { Product product = new Product(); product.Name = "ToyCar"; product.Price = 120; return product; } } class Logger { public void Log(Product product) { Console.WriteLine("Product {0} created at {1}.Price is {2}", product.Name, DateTime.UtcNow, 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(IProductFactory productFactory) { Box box = new Box(); Product product = productFactory.Make(); box.Product = product; return box; } } }
C#知识学习—14 方法参数进阶,扩展方法CS 61A(Part 11) - Mutable Sequences
Loading...
Kitety
Kitety
独特为佳,Kitety的个人博客
公告
我曾经七次鄙视自己的灵魂
--卡里·纪伯伦
第一次,当它本可进取时,却故作谦卑;
第二次,当它在空虚时,用爱欲来填充;
第三次,在困难和容易之间,它选择了容易;
第四次,它犯了错,却借由别人也会犯错来宽慰自己;
第五次,它自由软弱,却把它认为是生命的坚韧;
第六次,当它鄙夷一张丑恶的嘴脸时,却不知那正是自己面具中的一副;
第七次,它侧身于生活的污泥中,虽不甘心,却又畏首畏尾。
 
支持在线微信赞赏扶贫
notion image
 
最新评论
Loading...