前言
委托: 函数指针的“升级版”;(C/C++中的函数指针)
委托分类: Action委托、Function委托、自定义委托;
”一切皆地址“: 程序(本质) = 数据 + 算法;(变量(数据) 是以某个地址为起点的一段内存中所存储的值;函数(算法) 是以某个地址为起点的一段内存中所存储的一组机器语言指令)
Java 中没有与委托相对应的功能实体
什么是委托?
一句话解释就是:将方法当作参数传入另一个方法的参数。 .net中有很多常见的委托如:Func 、Action 作用:提高方法的扩展性
- 委托从字面上理解就是一种代理,类似于房屋中介,由租房人委托中介为其租赁房屋。
- 在 C#语言中,委托则委托某个方法来实现具体的功能。
- 委托是一种引用类型,虽然在定义委托时与方法有些相似,但不能将其称为方法。
- 委托在使用时遵循三步走的原则,即定义声明委托、实例化委托以及调用委托。
- 从数据结构来讲,委托是和类一样是一种用户自定义类型。
- 委托是方法的抽象,它存储的就是一系列具有相同签名和返回回类型的方法的地址。
- 调用委托的时候,委托包含的所有方法将被执行。
- 委托是 C# 语言中的一个特色,通常将委托分为命名方法委托、多播委托、匿名委托,其中命名方法委托是使用最多的一种委托。
三种委托的使用
Action 委托: 指向无返回值成员;
using System;
namespace InterfaceTest
{
internal class Program
{
public static void Main(string[] args)
{
Calculator calculator = new Calculator();
Action action = new Action(calculator.Report);//Action委托
calculator.Report();//直接调用
action.Invoke();//间接调用
action();//间接调用
}
}
class Calculator
{
public void Report()
{
System.Console.WriteLine("I have 3 methods");
}
public int Sum(int a, int b)
{
return a + b;
}
}
Function委托: 指向有返回值成员(Func<参数1类型,参数2类型,返回值类型>)
using System;
namespace InterfaceTest
{
internal class Program
{
public static void Main(string[] args)
{
Calculator calculator = new Calculator();
Func<int, int, int> func = new Func<int, int, int>(calculator.Sum);//func<>委托
calculator.Sum(2, 3);//直接调用
func.Invoke(2, 3);//间接调用
func(2, 3); //间接调用
}
}
class Calculator
{
public void Report()
{
System.Console.WriteLine("I have 3 methods");
}
public int Sum(int a, int b)
{
return a + b;
}
}
}
自定义委托: 【修饰符】 delegate 【返回类型】【名称】(【参数列表】) (例如:public delegate int Add(int x,int y))
using System;
namespace InterfaceTest
{
public delegate double Cal(double x, double y);//委托声明
class Program
{
static void Main(string[] args)
{
Calculator calculator = new Calculator();
Cal cal = new Cal(calculator.Sum);//委托实例化,方式一
Cal cal1 = calculator.Sub;//委托实例化,方式二
System.Console.WriteLine(cal(3, 2));
System.Console.WriteLine(cal1(3, 2));
}
}
class Calculator
{
public void Report()
{
System.Console.WriteLine("I have 3 methods");
}
public double Sum(double a, double b)
{
return a + b;
}
public double Sub(double a, double b)
{
return a - b;
}
}
}
委托的使用
模板方法:借用指定的外部方法产生结果(可最大限度重用代码)
- 相当于“填空题”;
- 常位于代码中部;
- 委托有返回值
using System;
namespace InterfaceTest
{
public delegate double Cal(double x, double y);//委托声明
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.MakeToCar);
Box box1 = wrapFactory.WrapProduct(func1); //调用产品封装方法,输出含产品名字信息的box类
Box box2 = wrapFactory.WrapProduct(func2);
System.Console.WriteLine(box1.Product.Name); //输出结果
System.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 MakeToCar()
{
Product product = new Product();
product.Name = "MakeToCar";
return product;
}
}
}
回调方法:调用指定的外部方法
- 相当于“流水线”;
- 常位于代码末尾;
- 委托无返回值
using System;
namespace InterfaceTest
{
public delegate double Cal(double x, double y);//委托声明
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.MakeToCar);
Logger logger = new Logger(); //声明产品记录方法
Action<Product> log = new Action<Product>(logger.Log);
Box box1 = wrapFactory.WrapProduct(func1, log); //调用产品封装方法,输出含产品名字信息的box类
Box box2 = wrapFactory.WrapProduct(func2, log);
System.Console.WriteLine(box1.Product.Name); //输出结果
System.Console.WriteLine(box2.Product.Name);
}
}
//定义记录产品信息类
class Logger
{
public void Log(Product product)
{
System.Console.WriteLine("Product'{0}' create at {1} Price is {2}.", product.Name, DateTime.UtcNow, product.Price); //utcNow为不带时区的时间形式
}
}
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.Invoke(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 MakeToCar()
{
Product product = new Product();
product.Name = "MakeToCar";
product.Price = 100;
return product;
}
}
}
委托的缺点
- 是方法级别的紧耦合,工作使用要慎重;
- 会使代码可读性下降,debug的难度增加;
- 把委托回调、异步调用和多线程纠缠在一起,会让代码变得难以阅读和维护;
- 委托使用不当会造成内存泄漏和程序性能下降
委托的高级使用
单播委托:一个委托封装一个方法;
using System;
using System.Threading;
namespace InterfaceTest
{
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();
}
}
class Student
{
public int ID { get; set; }
public ConsoleColor PenColor { get; set; }
public void DoHomework()
{
for (int i = 0; i < 3; i++)
{
Console.ForegroundColor = this.PenColor;
Console.WriteLine("Student {0} doing homework {1} hours", this.ID, i);
Thread.Sleep(1000);
}
}
}
}
结果
多播(Multicast)委托:一个委托封装多个方法,调用执行顺序为封装方法的先后顺序
using System;
using System.Threading;
namespace InterfaceTest
{
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 < 3; i++)
{
Console.ForegroundColor = this.PenColor;
Console.WriteLine("Student {0} doing homework {1} hours", this.ID, i);
Thread.Sleep(1000);
}
}
}
}
结果:同上
异步调用
同步调用和异步调用
- 直接同步调用:使用方法名;
- 间接同步调用:使用单播/多播委托的Invoke方法;
- 隐式异步调用:使用委托的BeginInvoke;
- 显示异步调用:使用Thread 或 Task
隐式异步调用(.Net Core 框架不适用,.Net Framework框架可以)
using System;
using System.Threading;
namespace InterfaceTest
{
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 < 5; 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 < 3; i++)
{
Console.ForegroundColor = this.PenColor;
Console.WriteLine("Student {0} doing homework {1} hours", this.ID, i);
Thread.Sleep(1000);
}
}
}
}
结果
显示异步调用(Thread / Task)
using System;
using System.Threading;
namespace InterfaceTest
{
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(stu1.DoHomework); //显示异步调用Thread
Thread thread2 = new Thread(stu2.DoHomework);
Thread thread3 = new Thread(stu3.DoHomework);
thread1.Start();
thread2.Start();
thread3.Start();
for (int i = 0; i < 5; 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 < 3; i++)
{
Console.ForegroundColor = this.PenColor;
Console.WriteLine("Student {0} doing homework {1} hours", this.ID, i);
Thread.Sleep(1000);
}
}
}
}
结果