我只是看着使用声明,我一直都知道它的作用但是直到现在还没有尝试使用它,我已经提出了以下代码:

 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 down。我错过了什么吗?

据我目前所知,它只是阻止我关闭并处理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();
}

编辑:为了明确,我在这里避免使用使用块,因为我认为登录这样的情况很重要。经验告诉我,你永远不知道会出现什么样的奇怪异常。记录这种情况可能有助于您检测到死锁,或者发现架构更改会影响您使用过少但很少测试的代码库或其他任何问题。

编辑2:有人可以说,在这种情况下,使用块可以包装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中使用Dispose()调用创建一个try / finally。为什么不给开发人员一个统一的处理和异常处理方式呢?

在这种情况下使用使用语句可能没有任何好处,如果您要进行 try / catch / <代码>最后阻止无论如何。如您所知, using 语句是 try / finally 处理 IDisposable 对象的语法糖。如果您将拥有自己的尝试 / finally ,您当然可以自己执行 Dispose

这实际上主要归结为风格 - 您的团队可能更熟悉使用语句或使用语句可以使代码看起来更清晰。

但是,如果使用语句的隐藏的样板仍然存在,请继续自行处理,如果这是您的偏好。

如果您的代码如下所示:

using (SqlCommand cmd = new SqlCommand(...))
{
  try
  {
    /* call stored procedure */
  }
  catch (SqlException ex)
  {
    /* handles the exception. does not rethrow the exception */
  }
}

然后我会重构它以使用try .. catch ..最后反而。

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块中执行某些操作,而不仅仅是忽略异常。

阐述Chris Ballance所说的,C#规范(ECMA-334第4版)第15.13节规定“使用声明”分为三个部分:获取,使用和处置。资源的使用隐式包含在包含finally子句的try语句中。这个finally子句处理资源。如果获取了空资源,则不会调用Dispose,也不会抛出任何异常。“

描述接近2页 - 值得一读。

根据我的经验,SqlConnection / SqlCommand可以通过很多方式生成错误,您几乎需要处理抛出的异常,而不是处理预期的行为。我不确定我是否想在这里使用using子句,因为我希望能够自己处理null资源的情况。

使用不是关于捕获异常。这是关于正确处理垃圾收集器视图之外的资源。

“使用”的一个问题是它不处理异常。 如果“使用”的设计者会添加“捕获”可选地,它的语法类似于伪代码,它会更有用:

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

}

仍然没有必要编写代码来处理 MyDisposableObj b / c它将由使用处理......

怎么样?

是的,您仍然需要捕获异常。使用块的好处是您要为代码添加范围。你说,“在这段代码中做一些事情,当它到达终点时,关闭并处理资源”

它根本不是完全必要的,但它确实使用您的代码定义了您对其他任何人的意图,并且它也有助于不会错误地打开连接等。

这里有很多很棒的答案,但我认为还没有这么说。

无论如何......“处理”方法将在“使用”中的对象上调用块。如果你输入一个return语句,或者抛出一个错误,那就是“Dispose”。将被召唤。

示例:

我创建了一个名为“MyDisposable”的类,它实现了IDisposable并简单地执行了Console.Write。即使在所有这些情况下,总是写入控制台:

using (MyDisposable blah = new MyDisposable())
{
    int.Parse("!"); // <- calls "Dispose" after the error.

    return; // <-- calls Dispose before returning.
}

编译器实际上将using语句更改为try / finally块,只要它实现了IDisposable接口,就会丢弃using块的参数。除了确保指定对象在超出范围时正确处理时,使用此构造实际上没有错误捕获。

如上面的 TheSoftwareJedi 所述,您需要确保正确处理SqlConnection和SqlCommand对象。将两者堆叠成一个使用块有点混乱,可能不会按照您的想法执行。

另外,请注意使用try / catch块作为逻辑。这是一种代码味道,我的鼻子特别不喜欢它,并且经常被新手或我们这些人急于在截止日期前使用。

仅供参考,在这个具体示例中,因为您正在使用ADO.net连接和Command对象,请注意using语句只执行Command.Dispose,而Connection.Dispose()实际上并不关闭连接,但只是将它释放回ADO.net连接池,以便下一个connection.open重用...这是好的,绝对正确的事情,如果你不这样做,连接将保持不可用直到垃圾收集器将其释放回池中,这可能直到许多其他连接请求,否则将被强制创建新连接,即使有未使用的连接等待垃圾收集。

我会决定何时何时不使用依赖于我正在处理的资源的using语句。在资源有限的情况下,例如ODBC连接,我更喜欢使用T / C / F,因此我可以在它们发生时记录有意义的错误。让数据库驱动程序错误冒泡回客户端并且可能在更高级别的异常包装中丢失是次优的。

T / C / F让您高枕无忧,资源按您希望的方式处理。正如一些人已经提到的那样,using语句不提供异常处理,它只是确保资源被破坏。异常处理是一种低估和低估的语言结构,通常是解决方案成功与失败之间的差异。

如果函数的调用者负责处理任何异常,则using语句是确保资源被清理的好方法,无论结果如何。

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

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

所以,基本上,“使用”与“Try / catch / finally”完全相同只有更灵活的错误处理。

示例的小修正: SqlDataAdapter 也需要使用语句在中实例化:

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

首先,您的代码示例应为:

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

但是你不需要处理清理 conn cmd ;它已经为你完成了。

使用

相同的事情
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

我知道我宁愿写些什么。 : - )

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top