委托:是一种数据类型,它的出现是为了解决在C/C++中需要使用到函数指针的情况。
- 在计算机中,一切皆地址:比如变量是数据的地址,变量指向以某个地址为起点的一段内存中所储存的值;比如函数是一堆指令的地址,函数指向以某个地址为起点的一段内存中所存储的一组机器语言指令
- 在程序中,我们调用函数的方式有两种:一种是直接调用 函数名(参数1,参数2) → CPU可以通过函数名获取函数所在的地址并开始执行,执行到结尾处返回;另一种是通过函数指针来调用 CPU通过读取函数指针存储的值获取函数所在的地址并开始执行,执行到结尾处返回。
- 以上都是为了帮助理解 委托是什么:C#中没有函数指针,而是使用委托来替代。
使用委托,能够间接调用方法。
- 1、准备一个无参数、返回值为void的方法
static void PrintInfo()
{
Console.WriteLine("哈哈哈");
}
- 2、在Main方法中 声明一个Aciton类型的变量 并且使用 PrintInfo方法创建 Action实例
Action action = new Action(PrintInfo);
- 3、通过Invoke方法,间接调用aciton指向的PrintInfo方法
action.Invoke();
- 4、输出结果:哈哈哈
以上通过一个简单示例,演示了如何通过委托调用目标方法;需要注意一点就是:用于实例化委托的方法必须与委托类型结构保持一致--Action是无参数、返回值void的委托类型、所以PrintInfo方法也必须是无参数、返回值void
委托间接调用方法的另一种写法:委托变量(参数1,参数2,..);
static bool IsBiggerThanFollow(int param1, int param2)
{
if (param1 > param2)
{
return true;
}
else
{
return false;
}
}
//在Main方法中调用
Func<int, int, bool> func = new Func<int, int, bool>(IsBiggerThanFollow);
var rtn_value = func(12, 13);
Console.WriteLine(rtn_value);
通过上面的例子可以看出,可以不使用Invoke方法,直接用委托变量名(传入需要的实参)也可以完成方法调用
自定义委托
- 委托是一种数据类型,但是它的声明方式和一般的类不同,访问控制修饰符 delegate关键字 目标方法返回值类型 委托类型名 (参数列表)
- 如:public delegate int Calculator(int x,int y);
- 声明委托类型,一般位于名称空间体中,和其它类同级;
- 例子:声明委托类型、实例化委托、调用委托
class Program
{
static void Main(string[] args)
{
// 实例化委托类型 并为 委托变量赋值
Caluc caluc = new Caluc(Add);
// 调用委托
int ss= caluc(1, 2);
Console.WriteLine(ss);
Console.ReadKey();
}
static int Add(int p1, int p2)
{
return p1 + p2;
}
}
//声明Caluc委托类型
public delegate int Caluc(int x, int y);
委托的常见使用场景:将方法作为参数传递给另一个方法
- 1、模板方法--特点:有返回值;
static void Main(string[] args)
{
List<int> lis = new List<int>() {123,14,3,4,17,55,5};
var ll= lis.Where(new Func<int, bool>(IsLessThanFive));
foreach (var item in ll)
{
Console.WriteLine(item);
}
Console.ReadKey();
}
static bool IsLessThanFive(int xx)
{
if (5.CompareTo(xx)>0)
{
return true;
}
else
{
return false;
}
}
上面演示的就是委托作为模板方法的场景;这里Func作为Where方法的参数传入,根据委托所包含的方法的返回值,来决定最后List中留下哪些项作为新集合的元素;
- 2、回调方法--特点:满足某些条件的时候,执行既定的操作,一般不需要返回值。
static void Main(string[] args)
{
Timer timer = new Timer(new TimerCallback(PrintTime), null, 0, 3000);
Console.ReadKey();
}
static void PrintTime(object param)
{
Console.WriteLine(DateTime.UtcNow+" "+"运行正常");
}
以上展示了一个定时回调的例子:TimerCallback是一个回调委托,PrintTime是与这个回调委托匹配的回调方法,每3秒,调用一次PrintTime方法
class Program
{
static void Main(string[] args)
{
//Controler controler = new Controler(() => { Console.WriteLine(DateTime.Now); });
Test test = new Test();
Controler controler = new Controler(test.PrintTime);
controler.Excute();
}
}
class Test
{
public void PrintTime()
{
Console.WriteLine(DateTime.Now);
}
}
class Controler
{
private Action _printTime;
public Controler(Action action)
{
_printTime = action;
}
public void Excute()
{
Console.WriteLine("点击任意键获取当前时间或者按ESC退出");
while (Console.ReadKey(true).Key != ConsoleKey.Escape)
{
_printTime.Invoke();
}
}
}
以上是一个比较规范的利用委托实现方法回调的示例,回调方法通常是外部的方法;但有时执行简单的逻辑时也可以使用Lambda表达式作为回调方法
程序运行的效果就是,当我们按下非ESC的任意一个键时,打印当前时间---满足某一条件时,才执行某一动作。--这就是回调
以上便是对委托的简单总结,记录下来以便以后查阅。