2011-04-03 51 views
2

我决定删除我的代码中的一些使用语句,所以我可以捕获特定的异常并处理手动处理资源。我已经重构了一些代码,使它更具可读性和可维护性,在实现了新的try/catch块之后,我仍然想知道它们是否已经正确地放置在手头的任务上。异常处理的位置 - C#

例子:

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; 
} 

在这个例子中,我只实现的try/catch周围的SQL操作本身,我这样做是因为它确保了被抛出可以注意到和资源配置的任何异常正确。然后我注意到这会让for循环打开异常,尽管提供的索引器将通过GUI进行保护和创建。

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

谢谢你的时间!

编辑:

我知道using语句将是理想的处理处置和资源管理方面然而,由于在这个问题开头提到我希望能够捕获特定类型的异常,特别是从SQLite组件生成的异常。

+0

备注:您使用字符串连接来构建查询,因此请小心这里的SQL注入攻击。在这种方法之外,传递给它的参数需要提前检查问题可能并不明显。直觉上,我期望数据访问类来处理,但这不是。 – David 2011-04-03 20:37:11

+0

我在所有方法中使用参数化查询,列由GUI生成,不能通过用户输入手动输入。感谢指针虽然:) – 2011-04-03 21:48:50

回答

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; 
+0

正是我需要知道的,清晰而简单。谢谢! – 2011-04-03 22:12:38

8

不要忘记null检查:

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

这可能是一个构造函数抛出异常,在对象不会被初始化的情况。

+0

啊,很好的抓住哈哈!谢谢。 – 2011-04-03 21:55:30

1

你用这个重构推出一个更大的问题可能是当你考虑当中间实体之一的创建失败会发生什么事情可以看出。例如,如果连接创建抛出,那么finally块中抛出的异常会发生什么情况,试图对所有这些空变量调用Dispose?至少要事先检查一下null,或者在你的终端里放一个try/catch。我会说你可以从使用Resharper中受益,而这将会指出这些问题。

1

我不知道这是否是最佳做法,但我通常会将try/catch语句放在可访问外部资源的代码块中,如数据库访问,文件I/O等,这些可能会因各种原因原因(资源不可访问,I/O错误等)。我不保护我控制的代码 - 这就是单元测试到位的地方

btw:你知道你可以用string.Join()替换你的循环吗?

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

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块处置它。

+1

有点愚蠢,你可以解释如何使用'using'语句,当他说他曾经使用它们,但删除它们。他还给出了删除“使用”陈述的理由。你真的读过他的帖子吗? – 2011-04-03 21:06:17

+0

我想我确实忽略了这句话 - 仍然对我没有意义 - try/catch并不禁止你使用'using'语句,你可以同时执行这两个操作。 – BrokenGlass 2011-04-03 21:16:07

+1

当你有一个try-finally场景时,你需要'using'语句。如果你有一个try-catch-finally的场景,我认为不使用'using',而是使用你必须放在那里的结构是完全有效的。 – Jan 2011-04-03 21:32:46