簡體   English   中英

在C#中處理異常的最佳實踐是什么?

[英]What are best practices for handling exceptions in C#?

我的網頁中有以下代碼:

btnTest_Click(object sender, EventArgs e)
{
    ...
    bool ret=myFunc(...);
    if (ret)
    {...}
    else
    {
        lblStatus.Text="Some Text";
        lblStatus.Visible=true;
    }
}

private bool myFunc(...)
{
    bool ret=false;
    try
    {
        ...
        ret=true;
    }
    catch (Exception ex)
    {
        lblStatus.Text="Other Text";
        lblStatus.Visible=true;
    }
    return ret;
}

如果myFunc中發生異常,則lblStatus始終顯示“某些文本”而不​​是“其他文本”。 這意味着myFunc中的catch塊實際上沒有任何意義。 我想知道如何修復此代碼以更好地處理異常?

更新:也許我的榜樣不是很好。 但是我的主要目的是詢問最佳實踐,以了解調用和被調用函數之間的異常處理。

為什么您的被調用函數將標簽文本設置為異常,而調用方將其設置為成功?

那是一個混雜的隱喻。 讓一方負責UI(關注點分離),而另一方負責工作。 如果希望被調用的函數具有容錯能力,請嘗試以下操作:

private bool myFunc(...)
{
  bool ret ;
  try
  {
    ...
    ret=true;
  }
  catch
  {
    ret = false ;
  }
  return ret;
}

然后,您的呼叫者可以執行以下操作:

bool success = myFunc(...) ;
lblStatus.Text = success ? "Some Text" : "Other Text" ;
lblStatus.Visible = success ;

if ( success )
{
  // do something useful
}

它顯示“ Some Text”,因為當myFunc發生異常時,它返回false。 然后進入btnTest_Click方法的else塊,在此處將lblStatus.Text再次設置為“ Some Text”。

因此,基本上,您將標簽的文本設置為“其他文本”,然后設置為“某些文本”。

您的catch子句正在做很多事情。 它捕獲每個異常並“忘記”異常,將其抑制到調用堆棧的其余部分。 可以很好地解決問題,但我將嘗試說明您的選擇:

您通常有3個選擇:

  1. 不在乎異常,讓上面的代碼處理它
  2. 小心記錄異常並使其傳播
  3. 異常在給定的上下文中具有其含義,因此不應傳播(這是您的情況)

我全部使用。

選項1

您可以只實現您的功能,如果發生異常,則意味着發生了一些錯誤,並且您只是希望您的應用程序失敗(至少到一定程度)

選項2

發生某些異常,您需要執行兩個(或什至兩個)之一

  • 記錄錯誤
  • 將異常更改為對調用者更有意義的另一個異常

選項3該異常是預料之中的,您知道如何完全對其進行反應。 例如,在您的情況下,我傾向於認為您不在乎異常的類型,而是希望通過對給定的文本設置一些控件來實現“良好的默認設置”。

結論

沒有銀彈。 為每種情況使用最佳選項。 然而,捕獲和“抑制” catch(Exception ex)很少見,如果經常看到,通常意味着不好的編程。

異常處理就可以了。 代碼的問題是,如果返回值為false ,那就是在出現異常的情況下,將"Some Text"字符串放入標簽中,因此它將替換catch塊中的消息。

切換案例:

if (ret) {
  // it went well, so set the text
  lblStatus.Text="Some Text";
  lblStatus.Visible=true;
} else {
  // an exception occured, so keep the text set by the catch block
}

這是一個復雜的問題,所以我將嘗試將其分解

  1. 在功能方面,我會盡量堅持“單一責任主體”。 它應該做一件明確的事情。
  2. 例外應該是例外。 因此,最好不要產生異常,而應在一定時間內處理它們。 例如,最好在嘗試使用變量之前將其測試為null (這將引發異常)。 異常可能很慢(尤其是拋出很多異常時)
  3. 我要說的是,您在哪里處理異常的問題歸結於異常是誰的責任。 如果myFunc要訪問遠程服務器並返回true或false狀態,則希望它能夠處理自己的IO異常。 它可能不會處理(或重新拋出)任何參數問題。 這與第1點有關。功能職責是處理連接過程,而不是提供正確的參數。 如果其他人(或健忘的人)以后嘗試使用該代碼,則隱藏某些異常可能會導致問題。 例如,在建立連接的myFunc中,如果您隱藏參數異常,則可能不會意識到自己傳遞了錯誤的參數

如果要通知您在其中一個函數中遇到特定類型的錯誤,建議您繼承Exception並創建自己的異常類。 我會在btnTest_Click()處理程序中放入try-catch塊,然后查找您的自定義異常類。 這樣,您將不會失去檢測myFunc()函數內部發生的任何錯誤的機會。

我通常會設置一個錯誤處理系統。 這是一個簡單的方法,但是可以將其包裝為一個基類。 如果需要的話,我可以告訴你。

List<string> _errors;

void init()
{
 _errors = new List<string>();
}

protected void Page_Load(object sender, EventArgs e)
{
  init();
}

btnTest_Click(object sender, EventArgs e)
{
    ...
    var result = myFunc(...);
    if (result)
    {...}
    else
    {
        if (_errors.Count > 0)
        {
          var sb = new StringBuilder("<ul>");
          foreach (string err in _errors)
          {
            sb.AppendLine(string.Format("<li>{0}</li>", err));
          }
          sb.AppendLine("</ul>");
          lblStatus.Text=sb.ToString();//Make this a Literal
        }
    }
}

private bool myFunc(...)
{
    var result = true;
    try
    {
        ...
        ...        
    }
    catch (Exception ex)
    {
        result = false;
        _errors.Add(ex.Message);
    }
    return result;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM