回调是一种由服务端提供一部反馈的机制,它在某些情况下会涉及到多线程或者为同步更新提供入口,在 C# 中如果要编写良好的回调那么就必须用委托来表示回调。委托我们经常用在事件处理的时候,我们也可以将委托用在其他地方,例如我们像以送伞的方式在类和类之间沟通的话,我们就可以使用委托,这是因为委托可以定义类型安全的回调可以在运行的时候配置回调目标,并且可以向多个客户端发出通知。
委托其实是一种对象,它包含指向方法的引用。所指向的方法既可以是静态的又可以是实例方法,在程序运行时可以配置一个或多个客户对象进行通信。
C# 为我们提供了一种简单的方法来使用委托和回调,这种方法就是 lambda 表达式 ,同时在 .NET 中很多委托形式使用的是 Predicate 、 Func<> 和 Action<> 。在这三种定义委托的形式中第一种形式可以用第二种形式来替代,也就是说 Predicate 是用来判断某条条件是否成立,而 Func 则会根据一些列参数求出某个结果,因此 Func<T,bool> 是等同于 Predicate 的。这里要注意虽然第一种形式可以用第二种形式来替代,但是编译器是不允许它们之间进行转换的,因为即使委托使用的是同一套参数和返回类型,编译器也会按照两个来计算。
Tip:我们常用的 LINQ 就是以委托为基础构建的,回调则用于处理 WPF 和 WinForm 中的跨线程封送。
在 C# 里需要注意的是所有的委托都是多播委托,也就是说会把添加到委托中的所有目标函数都视为一个整体去执行。那么这就导致了两个问题:
- 程序在执行这些目标函数时候很有可能发生异常,只要其中一个目标函数发生异常调用链就会中断,进而导致后面剩余的目标函数不被执行;
- 程序会把最后执行的目标函数的返回值作为整个委托的返回值,忽略前面那些目标函数的返回值。
要解决上述问题我们可以手动来执行委托。由于每个委托都是一列表的形式来保存其中的目标函数的,因此只要在这个列表上迭代把每个目标函数执行一遍。
总结
如果要在程序运行时执行回调,那么最好的办法是使用委托。