委托delegate是函数指针的升级版
一切皆地址
- 变量(数据)是以某个地址为起点的一段内存中所存储的值
- 函数(算法)是以某个地址为起点的一段内存中所存储的一组机器语言命令
直接调用与间接调用
- 直接调用:通过函数名来调用函数,cpu通过函数名直接获取函数所在的地址并开始执行
- 间接调用:通过函数指针来调用函数,cpu通过读取函数指针存储的值获得函数所在的地址并开始执行
委托的意思
说白了就是我把方法交给你,让你替我执行,我不执行。
我想杀个人,但是我不想自己动手,委托给小明,让小明帮我杀这个人。最后人被杀了,我还没有亲自动手就完成了我要做的事。
class Test
{
public int Add(int a,int b)
{
return a + b;
}
public int Sub(int a,int b)
{
return a - b;
}
public void Show()
{
Console.WriteLine("打印一段话");
}
}
Action与Func
C#类库自带的委托
static void Main(string[] args)
{
Test test = new Test();
test.Show();//这是直接使用
//传入一个没有返回值的方法名,可以有参数
Action action = new Action(test.Show);
//或者这种简写
Action action1 = test.Show;
action.Invoke();//执行委托
action();//简写
//泛型,类型是参数类型,最后一个是返回值类型
Func<int, int, int> func1 = new Func<int, int, int>(test.Add);
Func<int, int, int> func2 = new Func<int, int, int>(test.Sub);
//简写
Func<int, int, int> func3 = test.Sub;
//执行委托
int x = func1.Invoke(100, 200);
int y = func2(100, 200);
Console.WriteLine("{0},{1}",x,y);
}
delegate委托最基本的使用
自定义委托是一个类
class Program
{
static void Main(string[] args)
{
Test test = new Test();
//实例化委托对象,传入一个返回类型,参数类型和声明委托一样的方法名
Dele dele = new Dele(test.Add);
Dele dele1 = new Dele(test.Sub);
//使用委托
int a = dele.Invoke(100, 200);
int b = dele1(200, 200);
Console.WriteLine(a);
Console.WriteLine(b);
}
}
//声明委托,和类同级
public delegate int Dele(int a, int b);
委托的一般使用
模板方法
class Product
{
public string Name { get; set; }
}
class Box
{
public Product Product { get; set; }
}
class WrapFactory
{
//传入一个委托类型,这个委托返回一个Product对象
public Box WrapProduct(Func<Product> getProduct)
{
Box box = new Box();
//执行委托
Product product = getProduct.Invoke();
box.Product = product;//用委托返回的值赋给box对象的Product属性
return box;
}
}
class ProductFactory
{
//制作披萨
public Product MakePizze()
{
Product product = new Product();
product.Name = "披萨";
return product;
}
//制作玩具车
public Product MakeToyCar()
{
Product product = new Product();
product.Name = "玩具车";
return product;
}
}
static void Main(string[] args)
{
ProductFactory productFactory = new ProductFactory();//产品工厂
WrapFactory wrapFactory = new WrapFactory();//包装工厂
//本生产是由工厂来做的,现在工厂不想自己生产,找Func来代工生产
Func<Product> func1 = new Func<Product>(productFactory.MakePizze);//委托制作披萨
Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);//委托制作玩具车
//包装厂需要传入一个代工制作的Func对象
Box box1 = wrapFactory.WrapProduct(func1);
Box box2 = wrapFactory.WrapProduct(func2);
Console.WriteLine(box1.Product.Name);
Console.WriteLine(box2.Product.Name);
}
回调方法callback
//记录程序的运行状态
class Logger
{
public void Log(Product product)
{
Console.WriteLine("产品:{0}在{1}被创建了,价格是{2}",product.Name,DateTime.UtcNow,product.Price);
}
}
class Product
{
public string Name { get; set; }
public int Price { get; set; }
}
class Box
{
public Product Product { get; set; }
}
class WrapFactory
{
//传入一个委托类型,这个委托返回一个Product对象
public Box WrapProduct(Func<Product> getProduct,Action<Product> logCallback)
{
Box box = new Box();
//执行委托
Product product = getProduct.Invoke();
//如果一个产品的价格大于等于50就调用回调函数记录一下
if (product.Price >= 50)
{
logCallback.Invoke(product);
}
box.Product = product;//用委托返回的值赋给box对象的Product属性
return box;
}
}
class ProductFactory
{
//制作披萨
public Product MakePizze()
{
Product product = new Product();
product.Name = "披萨";
product.Price = 12;
return product;
}
//制作玩具车
public Product MakeToyCar()
{
Product product = new Product();
product.Name = "玩具车";
product.Price = 121;
return product;
}
}
static void Main(string[] args)
{
ProductFactory productFactory = new ProductFactory();//产品工厂
WrapFactory wrapFactory = new WrapFactory();//包装工厂
//本生产是由工厂来做的,现在工厂不想自己生产,找Func来代工生产
Func<Product> func1 = new Func<Product>(productFactory.MakePizze);//委托制作披萨
Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);//委托制作玩具车
Logger logger = new Logger();
//声明一个没有返回值的委托,把logger.Log委托给log,要传入的值是Product类型
Action<Product> log = new Action<Product>(logger.Log);
//包装厂需要传入一个代工制作的Func对象,还有回调函数log记录产品信息
Box box1 = wrapFactory.WrapProduct(func1,log);
Box box2 = wrapFactory.WrapProduct(func2,log);
Console.WriteLine(box1.Product.Name);
Console.WriteLine(box2.Product.Name);
}
总结
上面的把委托传入方法中。比如传入A方法的委托到B方法里面。其实就是在B方法里面调用A方法。为什么要用委托?和反射一个道理,要在B方法里面调用的方法一定是高度可定制的,就是说可以是A方法,可以是C方法。X方法。写代码时也不知道需要什么方法啊,不能写死,就让使用这个方法的人传一个方法过来,用什么方法你说了算,你把你要用的方法传给我,我调用就是。但是C#好像不能传递方法类型的参数,所以就发明了个委托,带传方法。
单播委托与多播委托
public class Student
{
public int ID { get; set; }//学生id
public ConsoleColor PneColor { get; set; }//控制台输出的颜色
public Student() { }
public Student(int id,ConsoleColor cc)
{
this.ID = id;
this.PneColor = cc;
}
public void DoHomework()
{
for(int i = 0; i < 2; i++)
{
Console.ForegroundColor = this.PneColor;
Console.WriteLine("学生{0}做作业{1}小时",this.ID,i);
Thread.Sleep(100);
}
}
}
static void Main(string[] args)
{
Student stu1 = new Student(1,ConsoleColor.Yellow);
Student stu2 = new Student(2, ConsoleColor.Green);
Student stu3 = new Student(3, ConsoleColor.Red);
//学生做作业委托给action来做
Action action1 = new Action(stu1.DoHomework);
Action action2 = new Action(stu2.DoHomework);
Action action3 = new Action(stu3.DoHomework);
//一个委托封装一个方法,就是单播委托
action1();
action2();
action3();
//一个委托封装多个方法就是多播委托
action1 += action2 += action3;
action1();
}
委托隐式异步调用
static void Main(string[] args)
{
Student stu1 = new Student(1,ConsoleColor.Yellow);
Student stu2 = new Student(2, ConsoleColor.Green);
Student stu3 = new Student(3, ConsoleColor.Red);
//学生做作业委托给action来做
Action action1 = new Action(stu1.DoHomework);
Action action2 = new Action(stu2.DoHomework);
Action action3 = new Action(stu3.DoHomework);
//自动生成一个分支线程,第一个参数是回调函数,调用完了这个方法后有什么后续的回调吗,没有就null
//第二个参数一般也是null
action1.BeginInvoke(null,null);
action2.BeginInvoke(null, null);
action3.BeginInvoke(null, null);
}
总结
委托可以用接口代替,面向接口编程