繁体   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