前言:Jon Jagger先生是一個經歷豐富的人,寫的文章簡單而發人深省。大家可以到他的網站上浏覽一番,必然受益匪淺。這篇文章雖然比較簡單,翻譯當中難免出現錯誤,希望大家多多指教。
PS:這篇文章非常簡單,如果你感覺到自己已經到達一定的水平,那麼,請不要浪費時間了^_^
一 、令人痛苦的程式化錯誤處理
異常還沒出現前,處理錯誤最經典的方式就是使用錯誤代碼檢查語句了。例如
public sealed class Painful
{
...
private static char[] ReadSource(string filename)
{
FileInfo file = new FileInfo(filename);
if (errorCode == 2342) goto handler;
int length = (int)file.Length;
char[] source = new char[length];
if (errorCode == -734) goto handler;
TextReader reader = file.OpenText();
if (errorCode == 2664) goto handler;
reader.Read(source, 0, length);
if (errorCode == -5227) goto handler;
reader.Close();
Process(filename, source);
return source;
handler:
...
}
}
這種編碼方式單調乏味,翻來復去,難以使用,看起來非常的復雜,而且還使得基本功能很不清晰。並且還很容易忽略錯誤(故意或者偶爾的遺忘)。現在好了,有很多來處理這種情況,但是其中必有一些處理方式要好過其他的。
二、關系分離
異常所能做到的最基本的事情就是允許你將錯誤和基本功能分離開來。換句話,我們能將上面的改寫如下:
...
public sealed class PainLess
{
public static int Main(string[] args)
{
try
{
string filename = args[0];
char[] source = ReadSource(filename);
Process(filename, source);
return 0;
}
catch (SecurityException caught) { ... }
catch (IOException caught) { ... }
catch (OutOfMemoryException caught) { ... }
...
}
private static char[] ReadSource(string filename)
{
FileInfo file = new FileInfo(filename);
int length = (int)file.Length;
char[] source = new char[length];
TextReader reader = file.OpenText();
reader.Read(source, 0, length);
reader.Close();
return source;
}
}
在轉化過程中,需要注意以下幾點:
1.以前使用數字作為錯誤代碼來描述錯誤(很失敗的一種做法,誰知道2342是什麼意思呢?),現在使用命名的異常類來描述(例如:SecurityException)。
2.異常類彼此之間的關系並沒有緊密的聯系在一起。相反的,用來描述某一類錯誤的整數代碼在整個錯誤描述代碼中必須是唯一。
3. 在ReadSource方法中沒有沒有拋出詳細說明。在C#中拋出說明並不是必須的。
然而,最值得注意是:比較兩段代碼,ReadSource變得非常的清晰、簡單、明了。它現在僅包含需要實現其基本功能的語句,沒有表現出明顯的錯誤處理。這是可以的,因為如果出現異常,調用堆棧就會自我展開。這個版本是我們想要的“理想”版本。
然而,異常允許我們接近這個ReadSource的理想版本,同時,又阻止我們到達它。ReadSource是一個編碼例子,它請求資源(一個TextReader),使用了資源(Read),並且釋放了資源(Close)。
問題是如果在請求資源的過程中出現了異常,那麼資源將不會被釋放。這個問題的解決是本文的一部分。不過,“理想“中的ReadSource版本仍然是有用的。我們將使用它做為下面幾個版本的ReadSource評價的參照物。