C#使用语句捕获错误
我只是在看using语句,我一直都知道它的作用,但直到现在还没有尝试使用它,我已经找到了以下代码:C#使用语句捕获错误,c#,using,using-statement,C#,Using,Using Statement,我只是在看using语句,我一直都知道它的作用,但直到现在还没有尝试使用它,我已经找到了以下代码: using (SqlCommand cmd = new SqlCommand(reportDataSource, new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))) { cmd.CommandType = CommandType.StoredProcedure;
using (SqlCommand cmd =
new SqlCommand(reportDataSource,
new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString)))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
cmd.Connection.Open();
DataSet dset = new DataSet();
new SqlDataAdapter(cmd).Fill(dset);
this.gridDataSource.DataSource = dset.Tables[0];
}
这似乎可行,但这有什么意义,因为据我所知,我仍然需要将其包含在try-catch块中,以捕获不可预见的错误,例如sql server关闭。我错过什么了吗
就我目前所见,它只是阻止我关闭和处理cmd,但由于仍然需要try-catch,因此会有更多的代码行。使用并不是捕获异常。这是关于正确处理垃圾收集器视图之外的资源。是的,您仍然需要捕获异常。使用块的好处是您正在为代码添加作用域。您的意思是,“在这段代码中做一些事情,当它结束时,关闭并处置资源”
这一点也不完全必要,但它确实定义了您对使用您的代码的其他任何人的意图,也有助于避免错误地打开连接等。此代码应如下所示,以确保及时关闭连接。仅关闭命令不会关闭连接:
using (SqlConnection con = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, con))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
cmd.Connection.Open();
DataSet dset = new DataSet();
new SqlDataAdapter(cmd).Fill(dset);
this.gridDataSource.DataSource = dset.Tables[0];
}
要回答您的问题,您可以在finally块中执行相同的操作,但这很好地限定了代码的范围,并确保您记得清理。在执行IO工作时,我的代码预期会出现异常
SqlConnection conn = null;
SqlCommand cmd = null;
try
{
conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString)
cmd = new SqlCommand(reportDataSource, conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
conn.Open(); //opens connection
DataSet dset = new DataSet();
new SqlDataAdapter(cmd).Fill(dset);
this.gridDataSource.DataSource = dset.Tables[0];
}
catch(Exception ex)
{
Logger.Log(ex);
throw;
}
finally
{
if(conn != null)
conn.Dispose();
if(cmd != null)
cmd.Dispose();
}
编辑:为了明确起见,我避免在这里使用块,因为我认为在这种情况下登录很重要。经验告诉我,你永远不知道会出现什么样的奇怪异常。在这种情况下登录可能会帮助您检测死锁,或者发现模式更改对代码库中很少使用和很少测试的部分的影响,或者任何其他问题
edit2:可以说,在这种情况下,using块可以包装try/catch,这是完全有效的,并且在功能上是等效的。这实际上可以归结为偏好。您是否希望以处理自己的处置为代价来避免额外的嵌套?或者您是否需要额外的嵌套来进行自动处理。我觉得前者更干净,所以我这样做。但是,如果我在工作的代码库中找到后者,我不会重写它
编辑3:我真的,真的希望MS能够创建一个更明确的using()版本,让它更直观地了解实际情况,并在这种情况下提供更多的灵活性。考虑下面的虚构代码:
SqlConnection conn = null;
SqlCommand cmd = null;
using(conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString),
cmd = new SqlCommand(reportDataSource, conn)
{
conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString);
cmd = new SqlCommand(reportDataSource, conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
cmd.Open();
DataSet dset = new DataSet();
new SqlDataAdapter(cmd).Fill(dset);
this.gridDataSource.DataSource = dset.Tables[0];
}
catch(Exception ex)
{
Logger.Log(ex);
throw;
}
using语句只是在finally中创建一个try/finally和Dispose()调用。为什么不给开发人员一种统一的处理和异常处理的方法呢?编译器实际上会将using语句更改为try/finally块,其中只要using块实现IDisposable接口,就会处理using块的参数。除了确保指定的对象在超出范围时得到正确的处理外,使用此构造确实不会导致错误捕获 正如上面的SoftwareJEDI所提到的,您需要确保SqlConnection和SqlCommand对象都被正确处理。将两者都堆叠到单个using块中有点混乱,可能无法实现您认为的功能
另外,请注意使用try/catch块作为逻辑。这是我的鼻子特别不喜欢的一种代码味道,新手或急于赶在最后期限之前的人经常使用这种味道。FYI,在这个特定示例中,由于您使用的是ADO.net连接和命令对象,请注意using语句只执行Command.Dispose和connection.Dispose()它实际上并没有关闭连接,只是将其释放回ADO.net连接池,供下一个连接重用。打开。。。这是好的,也是绝对正确的做法,如果不这样做,连接将保持不可用,直到垃圾收集器将其释放回池中,这可能要等到大量其他连接请求,否则将被迫创建新连接,即使有一个未使用的连接等待垃圾收集 关于Chris Ballance所说的内容,C#规范(ECMA-334第4版)第15.13节规定“使用声明分为三个部分:获取、使用和处置。资源的使用隐式包含在包含finally子句的try语句中。这个finally子句处理资源。如果获取了空资源,则不会调用Dispose,也不会引发异常。“ 描述接近2页-值得一读
根据我的经验,SqlConnection/SqlCommand可以以多种方式生成错误,以至于您几乎需要处理抛出的异常,而不是处理预期的行为。我不确定我是否希望在这里使用using子句,因为我希望自己能够处理空资源情况。这里有很多很好的答案,但我认为不是他的话已经说过了 不管怎样,都会在“using”块中的对象上调用“Dispose”方法。如果放入return语句或抛出错误,则会调用“Dispose” 例如: 我创建了一个名为“MyDisposable”的类,它实现IDisposable,只需执行一个Console.Write。即使在所有这些场景中,它也总是写入控制台:
using (MyDisposable blah = new MyDisposable())
{
int.Parse("!"); // <- calls "Dispose" after the error.
return; // <-- calls Dispose before returning.
}
使用(MyDisposable blah=new MyDisposable())
{
int.Parse(“!”;//如果代码如下所示:
using (SqlCommand cmd = new SqlCommand(...))
{
try
{
/* call stored procedure */
}
catch (SqlException ex)
{
/* handles the exception. does not rethrow the exception */
}
}
然后我会重构它,使用try..catch..finally代替
SqlCommand cmd = new SqlCommand(...)
try
{
/* call stored procedure */
}
catch (SqlException ex)
{
/* handles the exception and does not ignore it */
}
finally
{
if (cmd!=null) cmd.Dispose();
}
在这种情况下,我将处理异常,因此我别无选择,只能添加try..catch,我不妨加入finally子句并为自己保存另一个嵌套级别。请注意,我必须在catch块中执行某些操作,而不仅仅是忽略异常。如果调用
SqlConnection conn = null;
SqlCommand cmd = null;
try
{
conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString)
cmd = new SqlCommand(reportDataSource, conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
conn.Open(); //opens connection
DataSet dset = new DataSet();
new SqlDataAdapter(cmd).Fill(dset);
this.gridDataSource.DataSource = dset.Tables[0];
}
catch(Exception ex)
{
Logger.Log(ex);
throw;
}
finally
{
if(conn != null)
conn.Dispose();
if(cmd != null)
cmd.Dispose();
}
using (...MyDisposableObj...)
{
... use MyDisposableObj ...
catch (exception)
... handle exception ...
}
it could even have an optional "finally" clause to cleanup anything other than the "MyDisposableObj" allocated at the beginning of the "using" statement... like:
using (...MyDisposableObj...)
{
... use MyDisposableObj ...
... open a file or db connection ...
catch (exception)
... handle exception ...
finally
... close the file or db connection ...
}
using (SqlConnection conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
cmd.Connection.Open();
DataSet dset = new DataSet();
new SqlDataAdapter(cmd).Fill(dset);
this.gridDataSource.DataSource = dset.Tables[0];
}
try
{
using (SqlConnection conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
cmd.Connection.Open();
DataSet dset = new DataSet();
new SqlDataAdapter(cmd).Fill(dset);
this.gridDataSource.DataSource = dset.Tables[0];
}
}
catch (RelevantException ex)
{
// ...handling...
}
SqlConnection conn = null;
SqlCommand cmd = null;
try
{
conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString);
cmd = new SqlCommand(reportDataSource, conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
cmd.Connection.Open();
DataSet dset = new DataSet();
new SqlDataAdapter(cmd).Fill(dset);
this.gridDataSource.DataSource = dset.Tables[0];
}
catch (RelevantException ex)
{
// ...handling...
}
finally
{
if (cmd != null)
{
try
{
cmd.Dispose();
}
catch { }
cmd = null;
}
if (conn != null)
{
try
{
conn.Dispose();
}
catch { }
conn = null;
}
}
// And note that `cmd` and `conn` are still in scope here, even though they're useless