分开变与不变的代码
在处理文件时,我们希望写出的代码是健壮的。如果一个长时间运行的文件处理程序对文件的操作没有做到健壮性就会出现一些问题。
比如一个HTTP服务器,它主要是把文件打开然后读取其中的内容,发送到请求者。如果一旦网络连接出问题,从而导致正在传输的文件没有关闭,那么想修改这个文件的内容我们只能重启这个HTTP服务器了。
为了写出健壮的文件处理代码,一般我们都会用到try-catch-finally语句块:
2try
3{
4 file = new FileStream(path, mode, access);
5 //do something
6}
7catch (IOException e)
8{
9 throw e;
10}
11finally
12{
13 if (file != null)
14 {
15 file.Close();
16 }
17}
如果你的代码中充满了这种模式的代码是不是会觉得很烦人呢?如果要是读取一个文件的内容,可能真正有用的代码仅仅是几行代码而已。而为了健壮性,我们却要写上10行多的代码来处理异常和关闭文件。
是不是想到了对这些模式性的代码进行封装呢?不错!为了能少写点代码,对其进行封装是件好事,而且我们还可以集中处理这种模式性的代码。至于思路来说,我们把变化的内容作为方法的参数即可。在封装这种操作时处理变化的代码我们使用代理。
对这种模式性的操作封装起来也并不困难。
首先我们需要一个代理,这个代理包含了一个FileStream类型的参数:
delegate void ProcessFileStreamCallback(FileStream file);
虽然这个代理的名字很长,不过我们可以使用匿名函数来少打一些字。
接下来时我们的FileTemplate类,里面全是静态方法:
2{
3 public static void ProcessFile(string path,
4 FileMode mode,
5 FileAccess access,
6 ProcessFileStreamCallback callback)
7 {
8 FileStream file = null;
9 try
10 {
11 file = new FileStream(path, mode, access);
12 callback(file);
13 }
14 catch (IOException e)
15 {
16 throw e;
17 }
18 finally
19 {
20 if (file != null)
21 {
22 file.Close();
23 }
24 }
25 }
26
27 public static void OpenFile(string path,
28 FileAccess access,
29 ProcessFileStreamCallback callback)
30 {
31 ProcessFile(path, FileMode.Open, access, callback);
32 }
33
34 public static void CreateFile(string path,
35 ProcessFileStreamCallback callback)
36 {
37 ProcessFile(path, FileMode.CreateNew, FileAccess.Write, callback);
38 }
39}
40
代码看上去并不复杂是吧。这段代码真正的核心思想就是你是否可以想到把变动的代码作为代理传入到方法中去。
下面我们来看看怎么应用这些静态方法。如果向一个文件中写入一些文字,代码看起来是这个样子的:
2{
3 string text = @"何处望神州,满眼风光北固楼。
4 千古兴亡多少事,悠悠。
5 不尽长江滚滚流。
6 年少万兜鍪,坐断东南战未休。
7 天下英雄谁敌手?曹刘。
8 生子当如孙仲谋。";
9
10 FileTemplate.CreateFile(@"C:"text_test.txt", delegate(FileStream file)
11 {
12 StreamWriter s = new StreamWriter(file);
13 s.Write(text);
14 s.Flush();
15 Console.WriteLine("数据已写入。");
16 });
17}
18
在这里,我们使用了Stream类的连接。我们希望使用StreamWriter来写入文本,可我们的代理只传入FileStream类。我们可以向StreamWriter类的构造方法中传入FileStream对象来创建与这个FileStream对象连起来的StreamWriter对象。
在此注意Flush方法的调用,因为StreamWriter是有缓冲区的,而且我们并没有调用StreamWriter对象的Close方法,所以我们应该调用Flush方法来让缓冲区中的内容写入文件中。如果你钟情于Lambda表达式,那么你完全可以用Lambda表达来创建代理对象:
2{
3 StreamWriter s = new StreamWriter(val);
4 s.Write(text);
5 s.Flush();
6 Console.WriteLine("数据已写入。");
7});
8
至于对异常的处理,您可以使用try-catch块来捕捉CreateFile方法所抛出来的异常。而且您不会因为关闭文件而导致一些其他的问题。
当然,上面仅仅是一种设计的方法,至于异常处理,您还可以定义一个新的代理来把异常处理的代码封装起来传入CreateFile方法,在此仅仅是抛砖引玉。最后要声明一下,这种方法是从Java的Spring框架的Template上学来的,好不好用见仁见智。而且还可以使用模版设计模式来实现这个方法。不过C#提供了代理为啥不用呢。:)