什么是委托
Action泛型委托(Delegate)用来封装一个方法且方法不具有参数与返回值
Func泛型委托(Delegate),用来表示一个有返回值的方法,尖括号最后一个类型是返回值,前面的是参数
class Program { static void Main(string[] args) { Calculator calculator = new Calculator(); Action action = new Action(calculator.Report); calculator.Report(); //直接调用 action(); //间接调用 action.Invoke(); //间接调用,同action(); 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(x, y); //同z = func1.Invoke(x, y); Console.WriteLine(z); z = func2(x, y); Console.WriteLine(z); } } class Calculator { public void Report() { Console.WriteLine("I hava 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 类名 { 类体 }委托的声明:放在namespace下是最标准、最清晰的写法,放在 类 下则变成嵌套类
public delegate double Calc(double x, double y);public-公开访问
delegate-关键字告诉编译器我要声明委托了
double-目标方法的返回值类型
Calc-委托名
()-目标方法的参数列表
加了括号(),代表“立刻执行这个方法并拿到结果”;不加括号,代表“把这个方法本身当作一个对象传递出去”。Calc calc1 = new Calc(calculator.Add);与Calc calc1 = new Calc(calculator.Add());
使用委托的方法:
①声明委托;
②创建委托的实例,并将目标方法名称放入委托构造函数的参数列表里;
③执行委托实例的方法,例如Action.Invoke();或者直接用简写Action(),则可以通过委托实例的方法来间接调用目标方法;
namespace ConsoleHelloWorld { public delegate double Calc(double x, double y);//声明委托 class Program { static void Main(string[] args) { 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.Div); double a = 100; double b = 200; double c = 0; c = calc1(a, b); Console.WriteLine(c); c = calc2(a, b); Console.WriteLine(c); c = calc3(a, b); Console.WriteLine(c); c = calc4(a, b); Console.WriteLine(c); } } class Calculator { public double Add(double x, double y) { return x + y; } public double Sub(double x, double y) { return x - y; } public double Mul(double x, double y) { return x * y; } public double Div(double x, double y) { return x / y; } } }委托的使用
委托的一般使用(日常使用)
模板方法
日常使用委托时,一般把委托当作方法的参数传入方法中:
①实现方法:委托封装了方法1,然后将该委托当作参数传给方法2;
②好处简介:可以将可能产生变化的业务逻辑部分放在方法1中,而方法2当作一个“模板”(即模板方法),通过固定的委托间接调用方法1,这样当业务逻辑需求发生变化时,就不用该方法2的代码了,而只需要改方法1的代码,甚至直接将委托中封装的方法换一个以实现其它功能;(填空题)
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(); // 1. 先拿个空箱子 Product product = getProduct();// 2. 调用委托,拿到产品!唯一变的,填空题的空 box.Product = product; // 3. 把产品装进箱子里 return box; // 4. 把装好的箱子交出去 } } 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; } }回调方法
可以调用也可以不调用,用得着的时候就调用,用不着就不调用,可以动态的选择后续将被调用的方法。如:A给B一张名片,告诉B有需要可以打电话给A,同时又有C、D等也给B名片,B可以动态的选择后续需要谁的帮忙。
把委托类型的参数传到主调方法里,被传进主调方法的委托类型的参数内部会封装被回调的方法,主调函数根据自己的逻辑来确认要不要调用回调方法。回调方法常位于主调函数末尾,无返回值。
namespace ConsoleHelloWorld { 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) { //不带时区的UtcNow 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(); // 1. 先拿个空箱子 Product product = getProduct();// 2. 调用委托,拿到产品!唯一变的,填空题的空 if (product.Price >= 50) { logCallback(product); } box.Product = product; // 3. 把产品装进箱子里 return box; // 4. 把装好的箱子交出去 } } 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; } } }委托的高级使用
单播委托
1个委托封装1个方法。
using System; using System.Threading; namespace ConsoleHelloWorld { 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(); //action.Invoke(); action2(); action3(); } } 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).", this.ID, i); Thread.Sleep(1000); //线程休眠1000ms(1s) } } } }多播委托
1个委托中封装着不止1个方法
多播委托在调用的时候,它执行的顺序是按照封装方法的先后顺序来执行的
using System; using System.Threading; namespace ConsoleHelloWorld { 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(); } } 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).", this.ID, i); Thread.Sleep(1000); //线程休眠1000ms(1s) } } } }第一个运行起来的线程就是进程的主线程,其他线程叫做分支线程。
同步调用(单线程调用)
在同一个线程内调用方法时,方法的执行时前一个执行完,后一个才执行,在同一个线程内依次执行的方法调用叫做同步调用
直接同步调用:
using System; using System.Threading; namespace ConsoleHelloWorld { 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).", this.ID, i); Thread.Sleep(1000); //线程休眠1000ms(1s) } } } }单播委托的间接同步调用:
using System; using System.Threading; namespace ConsoleHelloWorld { 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(); action3(); 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).", this.ID, i); Thread.Sleep(1000); //线程休眠1000ms(1s) } } } }多播委托的间接同步调用:
异步调用(多线程调用)
在不同的线程中去调用方法,每个线程跟另外一个线程都互不相干(指的是逻辑上互不相干,现实工作中经常会遇到多个线程共享(同时访问)同一个资源(比如某个变量)的情况, 这时候如果处理不当就会产生线程间争夺资源的冲突),一个线程的开始和结束并不会影响另一个线程的开始和结束。
隐式异步调用
begininvoke自动生成分支线程去调用封装的方法
using System; using System.Threading; namespace ConsoleHelloWorld { 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).", this.ID, i); Thread.Sleep(1000); //线程休眠1000ms(1s) } } } }主线程与分支线程谁也不等谁,同时执行:
显式异步调用
自己动手生成多线程
using System; using System.Threading; namespace ConsoleHelloWorld { 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); 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).", this.ID, i); Thread.Sleep(1000); //线程休眠1000ms(1s) } } } }using System; using System.Threading; using System.Threading.Tasks; namespace ConsoleHelloWorld { 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).", this.ID, i); Thread.Sleep(1000); //线程休眠1000ms(1s) } } } }*用类声明一个类类型的属性/字段,和用类创建一个类实例的区别是什么?
本质上是一样的,用类创建一个实例,赋值左边的就是先声明了一个类类型的变量,其实也可以看做是一个字段(字段就是一种变量);