【C#】回调函数、委托和Lambda表达式的理解

  1. 什么是回调函数?
    回调函数是一种非常有用的编程机制,.NET Framework通过委托来提供回调函数机制。在.NET Framework中,回调方法的应用非常广泛,例如,登记回调方法获得各种各样的通知,如未处理的异常、窗口状态的变化、菜单项选择、异步操作等等。
    回调函数不是由该函数的实现方直接调用,而是在特定的条件或事件发生时,由另一方调用的,用于响应事件。在非托管C/C++中是通过函数指针来实现的,在C#中是通过委托来实现的。
    回调函数的意义是把调用者与被调用者分开,调用者(通知者)不关心谁被调用(接收者)。
    回调函数相当于非托管C/C++中的回调函数,但后者不是类型安全的。
    (可参考:C语言中函数指针和回调函数的详解
  2. 委托的本质
    委托的本质也是一个类,可以定义在类内部,也可在类外部。
    委托定义:
public delegate bool MyDelegate(int value);

编译后的完整类:

public class MyDelegate1 : System.MulticastDelegate//所有委托都派生自该类
{
    public MyDelegate1(object @object, IntPtr method);
    //代码中mydelegate(10),编译后是mydelegate.Invoke(10)
    public virtual void Invoke(int 32); 
    //BeginInvoke和EndInvoke用于异步编程模型(APM)中,实现在线程池中异步的执行委托
    public virtual IAsyncResult BeginInvoke(int value, AsyncCallback callback, object @object);
    public virtual void EndInvoke(IAsyncResult result);
}

一个委托可以回调多个方法,可通过Delegate.Combine和Delegate.Remove增加和删除回调方法,简化语法是+=和-=。
3. 很多委托的定义实际上相同,.NET Framework提供了几种通用的委托类型:

//Action,不带返回值的委托,最多可带16个参数
public delegate void Action();//非泛型委托
public delegate void Action<T>(T obj);
public delegate void Action<T1,T2>(T1 arg1, T2 arg2);
public delegate void Action<T1,T2,T3>(T1 arg1, T2 arg2, T3 arg3);
public delegate void Action<T1, ...,T16>(T1 arg1,..., T16 arg16);
//Func,带返回值的委托,最多带17个参数
public delegate TResult Func<TResult>(TResult obj);
public delegate TResult Func<T,TResult>(T arg1);
public delegate TResult Func<T1, ...,T16, TResult>(T1 arg1,..., T16 arg16);

建议尽量使用上述委托,可简化编程,减少系统中类型数量。
4. 委托的一些简化语法
1). 可在方法参数中指定回调方法的委托,使用时不必构造该委托对象,直接将回调方法传入即可

//WaitCallback是已知委托
namespace System.Threading
{
  public delegate void WaitCallback(object state);
}
public static bool QueueUserWorkItem(WaitCallback callBack)

public void Test()
{
	//可直接传入回调方法,不必创建委托对象
	ThreadPool.QueueUserWorkItem(DelegateMethod,2);
}
private void DelegateMethod(object o)
{
	Console.WriteLine(o.ToString());
}

2). 有时候不需要定义回调方法,通过lambda表达式实现

public void Test()
{
	//编译器在看到lambda表达式后,会自动定义一个新的私有方法,称为匿名函数(不同于匿名方法)
	//这里o代表传入的参数2,可随意命名
	ThreadPool.QueueUserWorkItem((o) =>
    {
         Console.WriteLine(o.ToString());
    }, 2);
	//下面这种方式会报错,因为obj为null(obj也是随意命名的)
	ThreadPool.QueueUserWorkItem(obj => Console.WriteLine(obj.ToString()));
}

lambda表达式中=>左侧是指定传给lambda表达式的参数名称,右侧是匿名函数主体。下面是一些lambda表达式:

Func<string> f = () => "Tom";//委托不接收任何参数,使用()
Func<int, string> f2 = (int n) => n.ToString();//委托接收一个int参数
Func<int, string> f22 = (n) => n.ToString();//简化版本,由编译器自行推断
Func<int, string> f222 = n => n.ToString();//简化版本,由编译器自行推断
Func<int, int, string> f3 = (int n1, int n2) => (n1 + n2).ToString();//委托接收两个int参数
Func<int, int, string> f31 = (n1, n2) => (n1 + n2).ToString();//简化版本

如果函数主体有多个语句组成,必须用{}将语句封闭。且lambda表达式一般用在某段代码主体只运行一次的的情况中,如果该主体在多处调用,理想方案是单独封装一个方法。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

woniudaidai

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值