C语言中的指针
C语言中的指针像潘多拉魔盒一样让人又爱又恨,其实可以将指针理解为一种数据类型,比如int就是内置的一种数据类型,而指针与int类似都是一种数据类型,int类型又可分为int_32,int_64,而指针类型其中一个就是函数指针。函数指针的使用示例如下:
#include <stdio.h>
// 1.typedef 的作用是给数据类型起一个新的名字,此处FunctionPoint就是函数指针类型的别名
typedef int (*FunctionPoint)(int,int);
// 2.函数指针的使用
// 2.1 实现测试函数,函数签名要与函数指针类型的函数签名一致
int test(int a,int b)
{
return a+b;
}
int main()
{
// 2.2 将test函数赋值给FunctionPoint声明的变量
FunctionPoint function = test;
// 2.3 调用 此语句将最终调用到test函数
int result = function(1,2);
printf("%d",result);
return 0;
}
C#中的委托
从上面C的例子中我们大概可以认识到,函数指针可以像整型(int)一样,定义变量,并对其赋值然后进行使用。
在C#中同样可以实现与C语言中相同的功能,而且功能更加丰富,那就是通过C#中的委托(Delegate),可以简单的将理解为是函数指针的升级版。关于委托的官方介绍如下:
委托是一种引用类型,表示对具有特定参数列表和返回类型的方法的引用。 在实例化委托时,你可以将其实例与任何具有兼容签名和返回类型的方法相关联。 你可以通过委托实例调用方法。
委托的声明
委托的声明需要使用到关键字delegate,示例如下:
public delegate int MyDelegate(int x, int y);
从反编译角度看委托
上面声明的委托类型经过ILSpy反编译后,会发现声明的委托类型实际上就是一个类,其中内容如下所示:
// IL代码
.class nested public auto ansi sealed MyDelegate
extends [mscorlib]System.MulticastDelegate
{
// 方法
.method public hidebysig specialname rtspecialname
instance void .ctor (
object 'object',
native int 'method'
) runtime managed
{
} // 方法 MyDelegate::.ctor 结束
.method public hidebysig newslot virtual
instance int32 Invoke (
int32 x,
int32 y
) runtime managed
{
} // 方法 MyDelegate::Invoke 结束
.method public hidebysig newslot virtual
instance class [mscorlib]System.IAsyncResult BeginInvoke (
int32 x,
int32 y,
class [mscorlib]System.AsyncCallback callback,
object 'object'
) runtime managed
{
} // 方法 MyDelegate::BeginInvoke 结束
.method public hidebysig newslot virtual
instance int32 EndInvoke (
class [mscorlib]System.IAsyncResult result
) runtime managed
{
} // 方法 MyDelegate::EndInvoke 结束
} // 类 MyDelegate 结束
可以从IL代码中提取到的关键信息如下:
- 委托类型最终的内部表现是一个类
- 委托继承MulticastDelegate
- 委托内置的方法Invoke,BeginInvoke()及EndInvoke
多播委托
C#中的委托本质是一个类,是一个对象,那么这个对象就可以封装各种各样的功能,其中多播委托就是一个经典的功能。多播委托的注册使用"+=",多播委托的删除使用"-=",以注册多播委托为例:
// 创建委托实例
MyDelegate myDelegate = new MyDelegate(TestAdd);
// 注册多播委托
myDelegate += TestSub;
多播委托的调用由两种方式
- 第一种全部调用
// 执行委托,result的结果为TestSub执行结果
int result = myDelegate(1, 2);
- 第二种遍历调用(可根据需求具体调用那个)
Delegate[] delegates = myDelegate.GetInvocationList();
foreach (MyDelegate item in delegates)
{
item(2, 2);
}
异步委托
委托的执行默认采用Invoke方式执行(同步方式调用),然而委托也可采用异步方式执行,使用BeginInvoke就可以异步方式执行:
// BeginInvoke的最后两个参数固定为AsyncCallback和object,前面剩余的参数和要执行的异步方法的参数列表一致
// AsyncCallback定义异步执行完成以后要执行的函数
// object为传入的状态在AsyncCallCompleted中可获取到
public static void AsyncCallCompleted(IAsyncResult ar)
{
//获取BeginInvoke传入的的state参数
string strState = (string)ar.AsyncState;
Console.WriteLine("传入的字符串是{0}", strState);
}
// 执行异步委托
IAsyncResult res = myDelegate.BeginInvoke(3, 3, new AsyncCallback(AsyncCallCompleted), "测试程序");
myDelegate.EndInvoke(res);
// 注:调用完BeginInvoke以后一定要调用EndInvoke
结束异步委托的常见四种场景
- 做一些其他操作,然后调用EndInvoke方法阻塞线程直到该方法完成。
- 使用IAsyncResult.AsyncWaitHandle属性,使用它的WaitOne方法阻塞线程直到收到WaitHandle信号,然后调用EndInvoke。
- 检查BeginInvoke返回值IAsyncResult的状态来决定方法是否完成,然后调用EndInvoke方法。
- 通过在BeginInvoke方法中传递AsyncCallback的回调方法中调用该委托的EndInvoke方法。(推荐)
C#默认封装好的委托
Action&Func
Action委托与Func委托最主要的区别在于,Action委托没有返回值,Func委托允许由返回值 参考
关注「CoderPro」公众号 ,在微信后台回复「C#委托」,获取示例代码