在處理文件時,我們希望寫出的代碼是健壯的。如果一個長時間運行的文件處理程序對文件的操作沒有做到健壯性就會出現一些問題。
比如一個HTTP服務器,它主要是把文件打開然後讀取其中的內容,發送到請求者。如果一旦網絡連接出問題,從而導致正在傳輸的文件沒有關閉,那麼想修改這個文件的內容我們只能重啟這個HTTP服務器了。
為了寫出健壯的文件處理代碼,一般我們都會用到try-catch-finally語句塊:
1FileStream file = null; 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類,裡面全是靜態方法:
1class 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
代碼看上去並不復雜是吧。這段代碼真正的核心思想就是你是否可以想到把變動的代碼作為代理傳入到方法中去。
下面我們來看看怎麼應用這些靜態方法。如果向一個文件中寫入一些文字,代碼看起來是這個樣子的:
1private static void ProcessFile() 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表達來創建代理對象:
1FileTemplate.CreateFile(@"C:"text_test.txt", val => 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#提供了代理為啥不用呢。:)