- 委托是一种类,类是数据类型,所以委托也是数据类型
//Action是一种类 Type t = typeof(Action); Console.Write(t.IsClass);
- 委托是函数指针的升级版
函数指针(C++):
#include//一个指向有两个参数和一个返回值的函数的指针 //这个指针只可以指向有两个int类型参数和一个int类型返回值的函数 //typedef 的作用在于,将这个指针定义为一种数据类型 //Calc 是给这个函数指针起的名称 typedef int(*Calc)(int a,int b); //函数(方法) int Add(int a,int b){ int result = a + b; return result; } //主函数 int main(){ int x = 100; int y = 100; int z = 0; //声明函数指针类型的变量 //将Add函数的地址赋值给这个变量 Calc calc = &Add; //1. 直接调用Add函数 z = Add(x,y); printf("%d+%d=%d",x,y,z); //2. 通过函数指针,间接调用Add函数 z = calc(x,y); printf("%d+%d=%d",x,y,z); //使控制台等待输入回车,便于观察控制台输出的结果 system("pause"); }
- C#中比较常见的两种委托类型
3.1 Action
一般用于指向没有返回值的方法
class Student
{
static void Main(string[] args)
{
//声明一个Action的变量,并创建一个实例
//这个实例指向的是一个没有参数和返回值的方法PrintInf
Action act = new Action(PrintInf);
//1. 直接调用方法
PrintInf();
//2. 使用委托间接调用方法
act.Invork();
//简便写法,模仿函数指针的书写格式
act();
}
public void PrintInf()
{
Console.WriteLine("My Name is Wednesday.");
}
}
3.2 Func
一般用于指向有返回值的方法
class Student
{
static void Main(string[] args)
{
//声明一个Func的变量,并创建一个实例
//这个实例指向的是一个具有两个int类型的参数和int类型返回值的方法Add
//前面两个int代表了方法具有的参数
//第三个int代表这个方法的返回值类型
Func func = new Func(Add);
int i = 100;
int j = 100;
int z = 0;
//1. 直接调用方法
z = Add(i,j);
//2. 使用委托间接调用方法
z = func.Invork(i,j);
//简便写法,模仿函数指针的书写格式
z = func(i,j);
}
public int Add(int x,int y)
{
return x + y;
}
}
- 自定义委托
委托是一种类,那么在自定义委托时,要把定义语句写在命名空间体内,如果写在类中,就变成了这个类的嵌套类了
namespace Study
{
//自定义一个委托,需要使用关键字:delegate
//这个委托指向一个具有两个int类型参数、返回值为int类型的方法
public delegate int Calc(int,int);
class Student
{
static void Main(string[] args)
{
//创建委托的变量和实例
//在创建实例时,这个实例的构造函数要求指向一个具有两个int类型参数、返回值为int类型的方法
Calc calc = new Calc(Add);
int i = 100;
int j = 100;
int z = 0;
//1. 直接调用Add方法
z = Add(i,j);
//2. 间接调用Add方法
z = calc.Invork(i,j);
//简便写法,模仿函数指针的书写格式
z = calc(i,j);
}
int Add(int x, int y)
{
return x + y;
}
}
}
- 委托的一般使用
5.1 模板方法
· 在方法中,根据委托所指向的方法返回的数据,来进行记算。
· 委托有返回值
static void Main(string[] args)
{
int x = 100;
int y = 100;
int z = 0;
Func act = new Func(Add);
z = 100 + act.Invork(x,y);
}
int GetNum(int x, int y)
{
return x + y;
}
5.2 回调方法
· 由主调方法决定是否调用被调方法
· 委托无返回值
static void Main(string[] args)
{
int x = 2;
int y = 3;
Action act = new Action(PrintInf);
if(x % 2 != 0)
act.Invork(x);
if(y % 2 != 0)
act.Invork(y);
}
void PrintInf(int x)
{
Console.WriteLine(x + "是一个单数.");
}
- 委托的高级使用
6.1 多播委托:一个委托指向多个方法
static void Main(string[] args)
{
Action act1 = new Action(PrintInf);
Action act2 = new Action(PrintInf);
Action act2 = new Action(PrintInf);
//将act2加到act1上;
act1 += act2;
//将act3加到act1上;
act1 += act3;
//只执行act1,这三个委托都会依次执行
act1.Invork();
}
void PrintInf()
{
Console.WriteLine("My name is Wednesday.");
}
6.2 隐式异步调用
多播委托的例子为同步调用,异步调用则是指多线程并行调用
· 使用多线程显示异步调用
static void Main(string[] args)
{
Thread thread1 = new Thread(new ThreadStart(PrintInf));
Thread thread2 = new Thread(new ThreadStart(PrintInf));
Thread thread3 = new Thread(new ThreadStart(PrintInf));
thread1.Start();
thread2.Start();
thread3.Start();
}
void PrintInf()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("My name is Wednesday,I'm {0} years old", i);
Thread.Sleep(1000);
}
}
运行结果:每条线程并发执行
· 使用委托隐式异步调用:
static void Main(string[] args)
{
Action act1 = new Action(PrintInf);
Action act2 = new Action(PrintInf);
Action act3 = new Action(PrintInf);
act1.BeginInvoke(null,null);
act2.BeginInvoke(null,null);
act3.BeginInvoke(null,null);
}
void PrintInf()
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("My name is Wednesday,I'm {0} years old", i);
Thread.Sleep(1000);
}
}
运行结果:
使用委托隐式异步调用,和使用多线程显示异步调用,结果都是一样的,那么隐式和显示的区别是什么?
这里说的隐式异步调用全称应该是:多线程隐式异步调用。
也就是说,通过委托的BeginInvoke()方法,我们实现了多线程异步调用,而实现多线程异步调用这一步骤的具体操作,是由编译器来实现的,并不是我们手动的创建多线程去实现的,所以称之为"隐式异步调用"。而多线程显示异步调用,也就是由我们直接手动创建多线程异步调用。
- 前面说到,我们C#的委托是C语言的函数指针的升级版,Java语言考虑安全问题,并不具有"指针"这一功能,那么在Java中,怎么实现委托的功能呢。
那就是interface,Java完全使用接口代替了委托,所以在C#中,我们也可以在不适合使用委托时,使用接口来代替委托。
7.1 委托的一些缺点:
· 高耦合,委托属于方法级别的耦合;
· 使代码可读性下降,增加debug难度;
· 使用不当可能造成内存泄漏,让程序性能下降:
当委托里封装着一个类的方法时,即使没有引用变量引用这个类,在内存中也会给这个类分配内容空间,从而造成内存泄漏,以及程序性能下降;
· 等;



