[英]Handling exceptions, is this a good way?
我們正在努力使用策略來正確處理應用程序中的異常。 這是我們的目標(總結):
我們已經提出了一個涉及通用特定於應用程序特定異常的解決方案,並且在一段代碼中就是這樣的:
try {
// Do whatever
}
catch(ArgumentNullException ane)
{
// Handle, optinally log and continue
}
catch(AppSpecificException)
{
// Rethrow, don't log, don't do anything else
throw;
}
catch(Exception e)
{
// Log, encapsulate (so that it won't be logged again) and throw
Logger.Log("Really bad thing", e.Message, e);
throw new AppSpecificException(e)
}
記錄所有異常,然后轉為AppSpecificException,以便不再記錄它。 最終它將到達最后的手段事件處理程序,如果必須,將處理它。
我對異常處理模式沒有太多經驗......這是解決我們目標的好方法嗎? 它有任何重大缺點或大紅色警告嗎?
注意:這樣做的一個缺點是,在第一次捕獲之后你失去了處理特定異常的能力(如果你調用一個調用另一個方法的方法而第二個拋出異常則你無法處理它)但是我發現我從來沒有這樣做過......我只處理一個深度的例外...
如果您在異常第一次拋出異常時記錄異常,則不會記錄完整的堆棧跟蹤。
處理異常(即修復它們),盡可能接近它們被拋出的時間。 盡快收集有關上下文的信息。 但允許異常傳播到實際可以處理的位置。 日志記錄是最后一種處理方式,因此應該在應用程序子系統的外層中進行。
這樣就不需要使用特定於應用程序的異常作為標記來記錄不應該開始捕獲的異常。
不記錄異常然后重新拋出 - 調用者負責處理/記錄您生成的任何異常。
僅捕獲異常以處理它(例如記錄它),或添加特定於上下文的信息。
這是解決異常處理問題的一種非常常見的方法(從更具體到更不具體)。
請記住,如果要捕獲特定問題,有一個通用的ApplicationSpecific異常來捕獲該應用程序/方法中發生的所有事情並不是一個好主意。 最后嘗試使用更具體的例外來擴展它。
重新拋出異常是好的,更好的是聲明拋出某些異常並讓調用者處理它們的方法。 這樣您就必須創建更少的代碼,並且可以集中控制某些控件。
解決堆棧跟蹤問題的第一個選項:
class AppSpecificException : ApplicationException
{
public string SpecificTrace { get; private set; }
public string SpecificMessage { get; private set; }
public AppSpecificException(string message, Exception innerException)
{
SpecificMessage = message;
SpecificTrace = innerException.StackTrace;
}
}
我不得不寫一個例子來理解問題並檢查堆棧跟蹤問題,這是我的代碼,注意button2_click方法,最后我的文本框顯示崩潰字符串和堆棧跟蹤:
private String internalValue;
private void Operation1(String pField)
{
if (pField == null) throw new ArgumentNullException("pField");
internalValue = pField;
}
private void Operation2(Object pField)
{
if (pField == null) throw new ArgumentNullException("pField");
internalValue = Convert.ToInt32(pField).ToString();
}
private void Operation3(String pField)
{
if (pField == null) throw new ArgumentNullException("pField");
internalValue = pField;
Operation2(-1);
}
/// <exception cref="AppSpecificException"><c>AppSpecificException</c>.</exception>
private void button1_Click(object sender, EventArgs e)
{
try
{
Operation1("One");
Operation2("Two");
Operation3("Three");
MessageBox.Show(internalValue);
}
catch (ArgumentNullException ex)
{
textBoxException.Text = ex.Message + (char) 13 + (char) 10 + ex.StackTrace;
}
catch (AppSpecificException ex)
{
//textBoxException.Text = ex.Message + (char)13 + (char)10 + ex.StackTrace;
throw;
}
catch (Exception ex)
{
textBoxException.Text = ex.Message + (char)13 + (char)10 + ex.StackTrace;
throw new AppSpecificException("crash", ex);
}
}
private void button2_Click(object sender, EventArgs e)
{
try
{
button1_Click(sender, e);
}
catch (AppSpecificException ex)
{
textBoxException.Text = ex.SpecificMessage + (char) 13 + (char) 10 + ex.SpecificTrace;
}
}
盡量避免創建新的異常和重新拋出,因為拋出異常會將堆棧跟蹤設置為拋出異常的位置。 做一個簡單的投擲。 在Eric Lippert的博客上看到太多的重復使用 。
我建議你更多考慮一下你想要用來“處理”的模式
如果您的處理模式歸結為記錄或重新拋出,則最終將記錄重新拋出的錯誤。 所以最后,它只是錯誤記錄。 如果您正在使用ASP.NET,請使用elmah,因此至少您的代碼不包含在try / catch-and-log樣板中。
只有幾種方法可以“處理”僅僅記錄日志的錯誤。
重試。 (注意無限循環)
等等並重試。
嘗試不同但等效的技術(無法連接http?嘗試連接https)。
建立缺少的條件(創建拋出FolderNotFoundException的文件夾)
忽略錯誤 - 請三思而后行,只有當錯誤不是真正的問題時才有意義,例如第三方庫是否警告您某個條件不適用。
關於異常處理的一個很好的解決方案是使用攔截。 但是,您必須根據體系結構驗證此模式是否可以應用於您的應用程序:攔截需要容器。
原理是通過在方法上使用屬性(自定義)將異常處理分解為方法,然后使用容器初始化實例。 容器將通過反射來代理這些實例:它的被調用實例攔截器。 你只需要像往常一樣通過這些實例調用你的方法,讓攔截機制完成你在方法之前或之后編寫的工作。
請注意,您可以在方法中添加try catch來管理攔截器中未受管理的特定異常。
Unity攔截: http : //msdn.microsoft.com/en-us/library/dd140045.aspx
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.