【问题标题】:Using exceptions for error handling flow?对错误处理流程使用异常?
【发布时间】:2012-07-05 13:34:16
【问题描述】:

我有以下模式:

private void MyFunction()
{
    doStuff();

    if (problem())
    {
        cleanup();
        return;
    }

    doMoreStuff();

    if (otherProblem())
    {
        cleanup();
        return;
    }

    doYetMoreStuff();
}

错误清除代码重复。消除这种情况的明显方法是:

private void MyFunction()
{
    try {
        doStuff();

        if (problem()) throw new MyException();

        doMoreStuff();

        if (otherProblem()) throw new MyException();

        doYetMoreStuff();
    }
    catch (MyException)
    {
        cleanup();
        return;
    }
}

但是,错误情况并不是真正的例外 - 这是一个 ASP.Net 页面,查询字符串中的错误数据或没有数据将触发错误情况。异常似乎是对错误处理进行重复数据删除并将其与主代码分开的最明显方法,但据我了解,公认的最佳实践是不要对这样的控制流使用异常。

这样做的习惯方法是什么?

【问题讨论】:

  • 如果您使用该 cleanup() 方法,那么代码不会在第一个实例中被复制?正如您所说,异常应该只用于例外情况。

标签: c# exception exception-handling


【解决方案1】:

在非异常情况下使用异常并不是一个好主意,尤其是当异常被抛出并在同一个方法中捕获时。更好的方法是使用一个布尔变量来指示需要清理,并在 finally 块内执行清理。

var needsCleanup = true;
try {
    doStuff();

    if (problem()) return;

    doMoreStuff();

    if (otherProblem()) return;

    doYetMoreStuff();

    needsCleanup = false;
} finally {
    if (needsCleanup) {
        cleanup;
    }
}

【讨论】:

  • @Steve 谢谢,在使用异常传递控制时,性能损失绝对是一个有效的问题。
【解决方案2】:

您可以使用 just try .. finally 和 early return。

private void MyFunction()
{
  try
  {
    doStuff();

    if (problem())
    {
        return;
    }

    doMoreStuff();

    if (otherProblem())
    {
        return;
    }

    doYetMoreStuff();
  }
  finally
  {
    cleanup();
  }
}

【讨论】:

    【解决方案3】:

    finally 块将在执行离开try 块时执行,即使它离开try 块的原因是因为您调用了return。所以,你可以这样做:

    private void MyFunction()
    {
        try
        {
            doStuff();
            if (problem()) return;
    
            doMoreStuff();
            if (otherProblem()) return;
    
            doYetMoreStuff();
        }
        finally
        {
            cleanup();
        }
    }
    

    【讨论】:

      【解决方案4】:

      如果cleanup() 真的只是一个方法调用而不是重复

      在这种情况下我不会使用异常。 您可以轻松地在谷歌上搜索详细描述为什么在正常流程中使用异常是一个坏主意的文章:主要是影响性能并且会搞砸性能计数器(“抛出的异常数”将毫无意义)。

      【讨论】:

      • cleanup 大约是三行。
      【解决方案5】:

      当发生意外的事情时应该抛出异常。

      考虑你想在你的数据库中添加一个用户,但如果用户名已经存在,你只需要这样说的错误。现在,错误不一定是异常!

      bool AddUser(string username)

      现在流程看起来像这样:

      if(AddUser(username)) {}
      else
      {
          // Notify the user that it didn't work
      }
      

      如果这里抛出异常,应该是因为软件无法连接数据库,而不是因为用户名已经存在。

      像这样为流控制抛出和捕获异常会导致比需要更多的开销,因此您应该使用if/else 来控制它。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-11-03
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-29
        相关资源
        最近更新 更多