本文转载自http://blog.youkuaiyun.com/liuyimu/article/details/5552349
C#回调机制实例解析
周四设计模式的课上,老师说到了回调机制的问题。本文实例解析了C#中回调机制的应用方法。
1.什么是回调
软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回。回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;异步调用是一种类似消息或事件的机制,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知,因此可知回调机制是实现异步调用的基础。因此,下面我们着重讨论回调机制在异步调用中的作用【1】。
下图展示了一次异步调用的结构图:
模块A对模块B进行异步调用,调用立即返回。待模块B中的方法执行完毕之后,通过回调机制(促发模块A中声明的回调函数)通知模块A调用完毕,并可以返回调用的结果。
2.C# Framework下异步调用
废话不说,先看一个C#中异步调用的实例,此实例来改编至金旭亮老师的《.net 2.0面向对象编程揭秘》一书,代码经过了适当的修改,从而符合图一所示的结构图。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.IO;
- namespace CallbackTest
- {
- class FileSize // 对应图一中的模块B
- {
- //计算指定文件夹的总容量
- public static long CalculateFolderSize(string FolderName)
- {
- if (Directory.Exists(FolderName) == false)
- {
- throw new DirectoryNotFoundException("文件夹不存在");
- }
- DirectoryInfo RootDir = new DirectoryInfo(FolderName);
- //获取所有的子文件夹
- DirectoryInfo[] ChildDirs = RootDir.GetDirectories();
- //获取当前文件夹中的所有文件
- FileInfo[] files = RootDir.GetFiles();
- long totalSize = 0;
- //累加每个文件的大小
- foreach (FileInfo file in files)
- {
- totalSize += file.Length;
- }
- //对每个文件夹执行同样的计算过程:累加其下每个文件的大小
- //这是通过递归调用实现的
- foreach (DirectoryInfo dir in ChildDirs)
- {
- totalSize += CalculateFolderSize(dir.FullName);
- }
- //返回文件夹的总容量
- return totalSize;
- }
- }
- class Program //对应于图一中的模块A
- {
- //定义一个委托
- public delegate long CalculateFolderSizeDelegate(string FolderName);
- // 初始化一个委派实例d,指向FileSize中类的CalculateFolderSize函数
- private static CalculateFolderSizeDelegate d = new CalculateFolderSizeDelegate(FileSize.CalculateFolderSize);
- //用于实现回调机制的回调函数,注意到该函数在模块一
- public static void ShowFolderSize(IAsyncResult result)
- {
- try
- {
- long size = d.EndInvoke(result);
- Console.WriteLine("/n文件夹{0}的容量为:{1}字节/n", (String)result.AsyncState, size);
- }
- catch (DirectoryNotFoundException e)
- {
- Console.WriteLine("您输入的文件夹不存在");
- }
- }
- static void Main(string[] args)
- {
- string InputFolderName;
- Console.WriteLine("请输入文件夹名称(例如:C://Windows),输入quit结束程序");
- InputFolderName = Console.ReadLine();
- d.BeginInvoke(InputFolderName, ShowFolderSize, InputFolderName);
- Console.WriteLine("Now calculating,Please waiting......"); //注意这一行代码
- Console.ReadLine();
- }
- }
- }
运行结果如下图所示:
请注意,在计算出文件夹的大小之前,也就是在CalculateFolderSize返回之前,Console.WriteLine("Now calculating,Please waiting......")这行代码已经被执行了,这就是异步调用的作用!
下面我们逐行解析这里面的代码。
首先看 public static void ShowFolderSize(IAsyncResult result)这个回调函数,注意到这是一个静态函数,返回值为void,参数类型为IAsyncResult(实际上是一个接口,详见MSDN),此参数包含外界传入的信息。
再看d.BeginInvoke(InputFolderName, ShowFolderSize, InputFolderName)这行代码,函数BeginInvoke的函数原型如下:
Public IAsyncResult BeginInove
{
<委托确定的方法签名中的参数列表,可能不止一个>,
AsyncCallback callback, object asyncState
}
其中第二个参数callback是异步调用结束时自动回调的函数,第三个参数asyncState用于向callback这个回调函数传递参数。
好了,现在整个程序的逻辑就很清楚了,在模块A中异步调用模块B中的CalculateFolderSize方法计算给定的文件夹的大小,并且指定模块A中的方法ShowFolderSize作为回调函数。
全文完
参考文献:【1】 陈家朋 异步消息传递——回调机制 IBM开发者网络 http://www.ibm.com/developerworks/cn/linux/l-callback/
【2】 金旭亮 《.NET 2.0 面向对象编程揭秘》 第13章 P454~P462