C# 使用语句捕获错误

29

我只是在看using语句,我一直知道它的作用,但直到现在才尝试使用它,我编写了以下代码:


I am just looking at the using statement, I have always known what it does but until now not tried using it, I have come up with the below code:
 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,所以代码行数将会更多。

16个回答

1

编译器实际上将using语句转换为try/finally块,其中使用块的参数被处理,只要它实现了IDisposable接口。除了确保指定的对象在超出范围时被正确处理之外,使用此结构并没有真正获得错误捕获。

如上所述,您需要确保SqlConnection和SqlCommand对象都被正确处理。将两者堆叠到单个using块中有点混乱,并且可能不会做您想要的事情。

此外,请注意不要将try/catch块用作逻辑。这是一种代码异味,我的鼻子特别讨厌,通常由新手或我们中的某些人匆忙赶工期而使用。


1

在这个特定的例子中,因为你正在使用 ADO.net 连接和 Command 对象,请注意 using 语句只执行 Command.Dispose 和 Connection.Dispose(),并没有真正关闭连接,而是将其释放回 ADO.net 连接池以供下一个 connection.open 重用...这是好的,也是绝对正确的做法,因为如果不这样做,连接将保持无法使用,直到垃圾收集器将其释放回池中,这可能要等到许多其他连接请求之后,否则这些请求将被迫创建新的连接,即使有一个未使用的连接在等待垃圾收集。


0

首先,你的代码示例应该是:

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/catch 中:

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

但是你不需要处理清理conncmd; 这已经为您完成了。

与没有using的相比:

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

我知道我更愿意写哪个。:-)


0

示例中有一个小修正:SqlDataAdapter 也需要在 using 语句中实例化:

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;
    con.Open();

    DataSet dset = new DataSet();
    using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
    {
        adapter.Fill(dset);
    }
    this.gridDataSource.DataSource = dset.Tables[0];
}

0
所以,基本上,“using”与“Try/catch/finally”完全相同,只是在错误处理方面更加灵活。

这并不是真的,使用using并不是一种花哨的替代try/catch/finally的方式,因为它不能提供“捕获”异常的方法。Using == Try/Finally(没有catch)。 如果你仍然需要捕获异常并进行一些自定义处理,你必须将using与try/catch块包装/嵌套在一起,或者完全用try/catch/finally替换它。 - devfreak
1
不行,Using 是一个“try/finally”语句的语法糖。除非你在语句中再包装一个 try/catch 或者嵌套一个 try/catch,否则你无法捕获任何抛出的异常;这在与非托管资源交互时是我认为很危险的事情。 - Jamie Keeling

0

如果调用您的函数的人负责处理任何异常,则使用语句是一种确保资源得到清理的好方法,无论结果如何。

它允许您在层/程序集边界处放置异常处理代码,并有助于防止其他函数变得过于混乱。

当然,这实际上取决于代码抛出的异常类型。有时候,您应该使用try-catch-finally而不是using语句。我的习惯是始终从IDisposables开始使用using语句(或者让包含IDisposables的类也实现该接口),并根据需要添加try-catch-finally。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接