3

我决定在我的代码中删除一些 using 语句,这样我就可以捕获特定的异常并手动处理资源的处置。在实现新的 try/catch 块之后,我重构了一些代码以使其更具可读性和可维护性,我想知道它们是否已正确放置以完成手头的任务。

例子:

public static DataTable Select(string table, string[] column, Object operand)
{
        DataTable dTable = null;
        SQLiteConnection connection = null;
        SQLiteCommand command = null;
        SQLiteDataReader dReader = null;

        //convert to array for arguments
        StringBuilder query = new StringBuilder();
        query.Append("select ");

        for (int i = 0; i < column.Length; i++)
        {
            query.Append(column[i]);

            if (i < column.Length - 1)
            {
                query.Append(",");
            }
        }
        query.Append(" from ");
        query.Append(table);

        try
        {
            connection = new SQLiteConnection(_connectionString);
            command = new SQLiteCommand(query.ToString(), connection);
            dTable = new DataTable();

            connection.Open();

            dReader = command.ExecuteReader();

            dTable.Load(dReader);

            return dTable;
        }
        catch (SQLiteException sqle)
        {
            //Handle exception
        }
        finally
        {
            connection.Dispose();
            command.Dispose();
            dReader.Dispose();
            dTable.Dispose();
        }
        return null;
}

在这个例子中,我只围绕 SQL 操作本身实现了 try/catch,我这样做是因为它确保可以注意到抛出的任何异常并正确处理资源。然后我注意到这使 for 循环对异常开放,尽管提供的索引器将受到保护并通过 GUI 创建。

我是否明智地将整个方法封装在 try/catch 语句中,还是我过于谨慎?您可以说我正在寻找管理语句本身位置的最佳实践。

谢谢你的时间!

编辑:

我知道 using 语句在处理资源的处置和管理方面是理想的,但是正如问题开头提到的,我希望能够捕获特定类型的异常,特别是那些从 SQLite 组件生成的异常。

4

6 回答 6

9

不要忘记null检查:

finally {
  if (connection != null) connection.Dispose();
  if (command != null) command.Dispose();
  if (dReader != null) dReader.Dispose();
  if (dTable != null) dTable.Dispose();
}

其中一个构造函数可能会引发异常,在这种情况下,对象将不会被初始化。

于 2011-04-03T20:28:50.793 回答
3

当您关心的是资源管理时,为什么要显式使用 try/catch?改用using

using(SQLiteConnection connection = new SQLiteConnection(_connectionString))
{ 
   ..
   using(SQLiteCommand command = new SQLiteCommand(query.ToString(), connection))
   {
      using(SQLiteDataReader  reader = dReader = command.ExecuteReader())
      {
          dTable.Load(dReader);
      }
   }
}

目前您正在返回dTable,但您正在将其丢弃在您的 finally 块中。

于 2011-04-03T20:33:51.137 回答
2

如果您认为有可能在数据库问题范围之外引发异常,则可以将整个方法包含在 try/catch 中。您可以通过捕获的内容轻松区分异常。例如:

DataTable dTable = null;
SQLiteConnection connection = null;
SQLiteCommand command = null;
SQLiteDataReader dReader = null;

try
{
  // non-DB code

  // DB code
}
catch (SQLiteException sqle)
{
  // Handle DB exception
}
catch (IndexOutOfRangeException ie)
{
  // If you think there might be a problem with index range in the loop, for example
}
catch (Exception ex)
{
  // If you want to catch any exception that the previous catches don't catch (that is, if you want to handle other exceptions, rather than let them bubble up to the method caller)
}
finally
{
  // I recommend doing some null-checking here, otherwise you risk a NullReferenceException.  There's nothing quite like throwing an exception from within a finally block for fun debugging.
  connection.Dispose();
  command.Dispose();
  dReader.Dispose();
  dTable.Dispose();
}
return null;
于 2011-04-03T20:31:43.197 回答
1

当您考虑在其中一个中间实体的创建失败时会发生什么时,可以看到您通过此重构引入的一个更大的问题。例如,如果连接创建抛出,那么在你的 finally 块中抛出的异常会发生什么,该异常试图在所有这些空变量上调用 Dispose?至少事先检查 null ,或者在你的 finally 中添加一个额外的 try/catch。我想说你可以从使用 Resharper 中受益,它已经指出了这些问题。

于 2011-04-03T20:32:11.217 回答
1

我不知道这是否是最佳实践,但我通常将 try/catch 语句放在访问外部资源(如数据库访问、文件 I/O 等)的代码块周围,这可能会由于各种原因(无法访问的资源)引发异常, I/O 错误等)。我不保护我控制的代码——这就是单元测试的地方

顺便说一句:您知道可以将循环替换为string.Join()?

更新:澄清一下:只有当你想捕获特定异常并执行一些自定义逻辑时,try/catch 块才真正有意义。否则你应该坚持using让异常冒泡并在适当的地方处理它(例如通知用户某些数据由于服务器不可用等而无法保存)

于 2011-04-03T20:32:38.317 回答
0

如果您所担心的只是正确处理物品,那么您应该为此使用“使用”块。 http://msdn.microsoft.com/en-us/library/yh598w02.aspx

于 2011-04-03T20:34:26.147 回答