我想生成一个打开SQL连接的方法:
public SqlConnection openConnection (string connString)
{
using (SqlConnection conn = new SqlConnection(connString))
{
try
{
conn.Open();
}
catch (Exception)
{
OrdersFaultException connEx = new OrdersDBInternalFaultException();
throw new FaultException<OrdersFaultException>(connEx);
return null;
}
return conn;
}
}
我想知道上面的代码是否正确?或者我应该在openConnection
中抛出异常,并在调用openConnection
的函数中处理异常?
答案 0 :(得分:2)
您应该处理相同抽象级别的异常,因为代码会引发这些异常。
这意味着,负责打开数据库的代码可能会抛出与数据库相关的异常,负责处理订单的代码可能会抛出与订单相关的异常。
您可能希望从 openConnection 方法中抛出类似 DatabaseConnectionException (带有相应的错误消息)的内容,然后引发 OrdersDBInternalFaultException 在发生 DatabaseConnectionException 的情况下,您需要处理 Orders 的地方。
附注:抛出异常后,您不需要返回null 。永远不会达到这种回报,这是一个死代码。
答案 1 :(得分:2)
您不能在此处使用 :没有人想要处置连接。 所以你可以使用模拟:
public SqlConnection openConnection (string connString) {
SqlConnection conn = null;
try {
conn = new SqlConnection(connString));
return conn;
}
catch (Exception e) { // <- Probably, you should catch more specific exception, e.g. DataException
// This "if" (that's a part of typical using emulation scheme) is redundant
// you may safely remove it unless there's no addition code in the "try"
// see Todd Bowles comments
if (!Object.ReferenceEquals(null, conn))
conn.Dispose();
// Do not forget to pass the reason (intitial exception e)
// when throwing your own one
throw new OrdersDBInternalFaultException(e);
}
}
答案 2 :(得分:1)
德米特里和蒂姆都是正确的。
对于异常处理虽然在您尝试打开连接的情况下,因此如果未实现所需的功能,则应抛出异常。因此,该用户知道函数必须执行的功能尚未执行。
答案 3 :(得分:1)
回到C ++时代,异常安全确实非常重要。如果我没记错的话,Sutter的Exceptional C ++在很多细节上对此进行了描述。
现在可能会让人感到奇怪的一件事就是C ++中没有“终极”。如果在堆栈上添加create对象,则在退出作用域时它将自动“处理”。说白了,范围是'{'和'}'为你管理的东西。
如果您正在使用C#,异常处理仍然很重要,但在不同的级别上。在所有情况下,您必须确保整体状态保持一致。那么这是如何工作的?
using
只不过是try
,catch
和finally
的包装。它基本上在Dispose
中创建finally
,从而确保当您退出范围(无论出于何种原因)时,对象将被清除(即,假设它实现IDisposable
)。因此,确保正确清理对象的最简单方法是:
(按此顺序!)
E.g:
using (var connection = new Connection()) // create instance
{
// use connection instance here
} // dispose connection instance -> implicitly created by 'using'.
重新使用代码
如果您不想再重复编写代码,这是一件好事(f.ex.与连接字符串),您可以手动处理对象,也可以使用简单的回调函数来保持流动相同。
private void Execute(Action<Connection> action)
{
using (var connection = new Connection()) // create instance
{
// use connection instance here
action(connection); // callback; this won't break the described mechanism
} // dispose connection instance -> implicitly created by 'using'.
}
为什么不正确使用using
所以这是一个返回连接而不考虑使用的实现:
// Buggy connection builder
private Connection CreateConnection()
{
Connection tmp = new Connection();
// do some stuff with tmp, might throw
return tmp;
}
private void Client()
{
using (var connection = CreateConnection())
{
// do something with the connection, assuming we're safe
}
}
这似乎一切都很好,但实际上是不正确的。你能看到吗?
在它说“用tmp做一些东西”的部分,有些东西可能出错,导致方法抛出。发生这种情况时,永远不会将对象返回给客户端,只留下没有引用的活动Connection
。即使你认为你是安全的,你实际上也不是。
关于构造函数和异常
如果你注意了,这也意味着构造者可能会出错。如果你创建一个没有正确清理的对象,如果狗屎击中了风扇,你还有一个悬空物体。例如:
// Buggy connection wrapper
public class ConnectionWrapper : IDisposable
{
public ConnectionWrapper()
{
this.connection = new Connection();
// do some other stuff, some of which might throw
}
private Connection connection;
// ...
// IDisposable stuff.
}
private void Client()
{
using (var connection = new ConnectionWrapper())
{
// do something with the connection, assuming we're safe
}
}
同样,构造函数中的代码可能会抛出异常。在这种情况下,构造函数不会将对象返回给客户端,这意味着IDisposable
中的finally
将不会调用using
。这并不意味着IDisposable
在这里是错误的 - 事实上它不是!
这里出现的问题是我们创建了一个IDisposable
对象,该对象未被返回且未被清除。我们可以通过添加catch
来解决此问题,只需在清理连接后重新抛出异常。