什么是委托

Action 泛型委托(Delegate)用来封装一个方法且方法不具有参数与返回值
Func 泛型委托(Delegate),用来**表示一个有返回值的方法,**尖括号最后一个类型是返回值,前面的是参数
cs
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;
}
}
委托的声明(自定义委托)

一般的类声明:
cs
class 类名
{
类体
}
委托的声明:放在 namespace 下是最标准、最清晰的写法,放在 类 下则变成嵌套类
cs
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(),则可以通过委托实例的方法来间接调用目标方法;
cs
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的代码,甚至直接将委托中封装的方法换一个以实现其它功能;(填空题)
cs
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可以动态的选择后续需要谁的帮忙。
把委托类型的参数传到主调方法里,被传进主调方法的委托类型的参数内部会封装被回调的方法,主调函数根据自己的逻辑来确认要不要调用回调方法。回调方法常位于主调函数末尾,无返回值。
cs
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个方法。
cs
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个方法
多播委托在调用的时候,它执行的顺序是按照封装方法的先后顺序来执行的
cs
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)
}
}
}
}
第一个运行起来的线程就是进程的主线程,其他线程叫做分支线程。
同步调用(单线程调用)
在同一个线程内调用方法时,方法的执行时前一个执行完,后一个才执行,在同一个线程内依次执行的方法调用叫做同步调用

直接同步调用:
cs
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)
}
}
}
}
单播委托的间接同步调用:
cs
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自动生成分支线程去调用封装的方法
cs
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)
}
}
}
}
主线程与分支线程谁也不等谁,同时执行:

显式异步调用
自己动手生成多线程
cs
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)
}
}
}
}
cs
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)
}
}
}
}
*用类声明一个类类型的属性/字段,和用类创建一个类实例的区别是什么?
本质上是一样的,用类创建一个实例,赋值左边的就是先声明了一个类类型的变量,其实也可以看做是一个字段(字段就是一种变量);