Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#使用语句捕获错误_C#_Using_Using Statement - Fatal编程技术网

C#使用语句捕获错误

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语句,我一直都知道它的作用,但直到现在还没有尝试使用它,我已经找到了以下代码:

 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