【问题标题】:Programmatic check or try...catch程序化检查或尝试...捕获
【发布时间】:2015-04-14 22:42:17
【问题描述】:

我这里有一些代码:

public static void OpenConnection(IDbConnection connection)
{
    if(connection == null)
        throw new ArgumentNullException("connection", "The connection was null.");

    if (connection.State != ConnectionState.Closed)
        connection.Close();
}

代码必须执行很多次,因为每次我在数据库中做某事时都会打开和关闭连接。我想知道下一个代码在性能方面是否会更好:

public static void OpenConnection(IDbConnection connection)
{
    try
    {
        connection.Close();
    }
    catch (NullReferenceException nullReferenceException) { throw; }
    catch (Exception exception) { } // This will occur if the connection was already closed so nothing should be done then.
}

PS。 catch (Exception exception) { } 有必要吗?

编辑:在第二个代码中将ArgumentNullException 替换为NullReferenceException,因为当connection == null 出现时,这将是异常。

【问题讨论】:

  • 是的,第二次捕获是必要的,因为您的第一次捕获只会捕获 ArgumentNullException 类型的异常,但不会捕获其他类型的异常(如果有)
  • 回答你的 p.s. - 没必要 - 它有害。它正在吞噬任何其他类型的异常。
  • 你通常将 connection.open 代码放在 try 块中,将 connection.close 放在 finally 块中。第二个 catch 不是必需的,但对于错误记录很有用。

标签: c# exception-handling


【解决方案1】:

我想知道下一个代码在性能方面是否会更好

考虑每种情况下的性能和功能差异:

  1. connection 为空

您将获得NullReferenceException 而不是ArgumentNullException,这是功能上的差异,因为您获得了不同的异常类型(以及less 关于异常发生原因/位置的上下文)。如果您决定捕获 NullReferecenException 并抛出 ArgumentNullException,那么您将有抛出 new 异常的开销,因此会影响性能。

  1. 连接未关闭。

尝试关闭连接 - 这里没有真正的性能或功能差异。

  1. 连接已关闭

您尝试关闭连接再次。在这里可能不是一个巨大的功能差异(因为如果您尝试关闭已经关闭的连接,大多数提供商可能不会生气),但它是 不必要的 并且可能有一些性能劣势,具体取决于 @ 987654326@ 方法实际上确实

所以您的第二种方法在功能上存在差异,实际上可能在性能方面存在缺点

坚持使用更清晰地说明预期行为的代码 - 然后仅在您有可衡量、可纠正的性能问题时进行优化。

【讨论】:

  • 将 ArgumentNullException 替换为 NullReferenceException,因为这将是连接 == null 时的异常。这是我的一个错误。你能更新你的答案吗?
  • @Krowi 我已更新,但请注意,NullReferenceException 通常比ArgumentNullException更糟糕,因为您不知道(无需检查堆栈跟踪并查看源代码) 为什么会发生异常。至少有了ArgumentNullException,您就知道是哪个参数导致了异常。
【解决方案2】:

除了 JDB 认为异常代价高昂的论点之外,请仔细查看您的代码并告诉我其中哪些更易于阅读/遵循?

如果您从未见过一种方法并且它以“尝试”开头,那么您真的需要仔细思考并仔细观察。但是,如果它以您的保护子句(if (connection == null) 部分)开头,顺便说一句,这是很常见的事情,您将立即看到,甚至不必考虑如果您将 null 传递到方法你会得到一个例外。把这个保护条款当作合同。你永远不希望 null 在那里传递。这是更好的设计。

关于“PS”。如果您要这样做,请记住所有其他可能在connection.Close() 中抛出的异常都将被捕获,除非您这样做,否则永远不会出现。这样的事情可能会让您的应用程序产生难以追踪的错误。

【讨论】:

    【解决方案3】:

    根据 Microsoft 的说法,异常对性能造成巨大影响。在合理的情况下尽量避免它们:

    抛出异常可能非常昂贵,因此请确保不要抛出太多异常。使用 Perfmon 查看您的应用程序抛出了多少异常。您可能会惊讶地发现应用程序的某些区域抛出的异常比您预期的要多。为了获得更好的粒度,您还可以使用性能计数器以编程方式检查异常编号。

    找到并设计掉异常密集的代码可以带来不错的性能胜利。 请记住,这与 try/catch 块无关:只有在引发实际异常时才会产生成本。 您可以使用任意数量的 try/catch 块。无缘无故地使用异常是你失去性能的地方。例如,您应该远离使用异常进行控制流之类的事情。

    https://msdn.microsoft.com/en-us/library/ms973839.aspx#dotnetperftips_topic2

    您的第二个示例实际上是一个更糟糕的情况。您几乎不应该捕获一般异常。您可能认为您知道会抛出什么,但很有可能会抛出一些意想不到的东西,从而导致系统不稳定和可能的数据丢失/损坏。

    使用 Catch 仍然是错误的(例外 e)

    尽管 CLR 异常系统将最严重的异常标记为 CSE,但在代码中编写 catch (Exception e) 仍然不是一个好主意。异常代表了一系列意外情况。 CLR 可以检测到最严重的异常——表明进程状态可能已损坏的 SEH 异常。但是,如果忽略或一般处理其他意外情况,它们仍然可能是有害的。

    在没有进程损坏的情况下,CLR 提供了一些关于程序正确性和内存安全性的非常强大的保证。在执行以安全的 Microsoft 中间语言 (MSIL) 代码编写的程序时,您可以确定程序中的所有指令都将正确执行。但是做程序指令说要做的事情往往不同于做程序员想要做的事情。根据 CLR 完全正确的程序可能会破坏持久状态,例如写入磁盘的程序文件。

    https://msdn.microsoft.com/en-us/magazine/dd419661.aspx

    【讨论】:

      【解决方案4】:

      您的第二个解决方案在性能方面并不好,因为当 try 块导致异常时,您的应用程序会更加努力,catch 块将尝试捕获异常。但是第二种解决方案在逻辑上要好得多。

      在您的第一个解决方案中,当连接为空时,您将在第一次检查时收到错误。

      Try-Catch 或 Try-Catch-Finally 是处理错误的强大工具,但价格昂贵。查看此链接以了解您可以使用它做什么:Using Try... Catch..., Finally!

      为了获得更好的性能,我会使用:

      private static void OpenSqlConnection(string connectionString)
      {
      using (SqlConnection connection = new SqlConnection(connectionString))
      {
          connection.Open();
          //Do some work
      }
      }
      

      上面的例子创建了一个SqlConnection,打开它,做了一些工作。 连接会在 using 块的末尾自动关闭

      对于你的 Try-catch 代码,我会做(捕捉异常):

      try
      {
      conn.Close();
      }
      catch (InvalidOperationException ex)
      {
      Console.WriteLine(ex.GetType().FullName);
      Console.WriteLine(ex.Message);
      //for Asp.net app
      //Response.Write(ex.GetType().FullName);
      // Response.Write(ex.Message);
      }
      

      try catch 请看:this link - Best Practices for Exceptions

      【讨论】:

      • 有趣的方法。对于这个具体的例子,我可以在我的初始化中也做SqlConnection connection = new SqlConnection(connectionString)并做using(connection)吗?
      • 是的。在您的代码中,您只是关闭连接。我的使用示例展示了如何打开和关闭连接(主要是如何在没有任何代码的情况下释放资源)。请在此处查看使用示例:stackoverflow.com/questions/4717789/…
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-06-13
      • 1970-01-01
      • 1970-01-01
      • 2012-06-26
      • 2011-05-06
      • 2019-08-25
      • 2019-04-14
      相关资源
      最近更新 更多