C# using 语句捕获错误

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/248961/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 19:50:35  来源:igfitidea点击:

C# using statement catch error

c#usingusing-statement

提问by PeteT

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

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

This seems to work but is there any point in this since as far as I can tell I would still need to enclose this in a try catch block to catch unforseen errors e.g. sql server down. Am I missing something?

这似乎有效,但是否有任何意义,因为据我所知,我仍然需要将其包含在 try catch 块中以捕获不可预见的错误,例如 sql server down。我错过了什么吗?

As far as I can currently see it just stops me closing and disposing of cmd but there will be more lines of code due to the try catch still being needed.

就我目前所见,它只是阻止我关闭和处理 cmd,但由于仍然需要 try catch,将会有更多的代码行。

采纳答案by TheSoftwareJedi

This code should be as follows to ensure timely closing of the connection. Closing just the command doesn't close the connection:

此代码应如下所示,以确保及时关闭连接。仅关闭命令不会关闭连接:

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

To answer your question, you can do the same in a finally block, but this scopes the code nicely and ensures that you remember to clean up.

要回答你的问题,你可以在 finally 块中做同样的事情,但这很好地限定了代码的范围并确保你记得清理。

回答by Amy B

using isn't about catching exceptions. It's about properly disposing of resources that are outside the view of the garbage collector.

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

回答by JamesSugrue

Yes you would still need to catch exceptions. The benefit of the using block is you are adding scope to your code. You are saying, "Within this block of code do some stuff and when it gets to the end, close and dispose of resources"

是的,您仍然需要捕获异常。using 块的好处是您可以为代码添加范围。你是说,“在这个代码块中做一些事情,当它结束时,关闭并处理资源”

It's not completely necessary at all, but it does define your intentions to anyone else using your code, and it also helps not leaving connections etc open by mistake.

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

回答by Jason Hymanson

When doing IO work I code to expectan exception.

在进行 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();
}

Edit:To be explicit, I avoid the usingblock here because I believe it to be important to log in situations like this. Experience has taught me that you never know what kind of weird exception might pop up. Logging in this situation might help you detect a deadlock, or find where a schema change is impacting a little used and little tested part of you code base, or any number of other problems.

编辑:明确地说,我在这里避免使用 using块,因为我认为在这种情况下登录很重要。经验告诉我,你永远不知道可能会弹出什么样的奇怪异常。在这种情况下登录可能会帮助您检测死锁,或者找到架构更改在哪里影响您的代码库中很少使用和测试的部分,或任何数量的其他问题。

Edit 2:One can argue that a using block could wrap a try/catch in this situation, and this is completely valid and functionally equivalent. This really boils down to preference. Do you want to avoid the extra nesting at the cost of handling your own disposal? Or do you incur the extra nesting to have auto-disposal. I feel that the former is cleaner so I do it that way. However, I don't rewrite the latter if I find it in the code base in which I am working.

编辑 2:有人可以争辩说,在这种情况下 using 块可以包装 try/catch,这是完全有效且功能等效的。这真的归结为偏好。您想以处理自己的处置为代价来避免额外的嵌套吗?或者您是否会产生额外的嵌套以进行自动处理。我觉得前者更干净,所以我这样做了。但是,如果我在我工作的代码库中找到后者,我不会重写它。

Edit 3:I really, really wish MS had created a more explicit version of using() that made it more intuitive what was really happening and given more flexibility in this case. Consider the following, imaginary code:

编辑 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;
}

A using statement just creates a try/finally with Dispose() calls in the finally. Why not give the developer a unified way of doing disposal and exception handling?

using 语句只是在 finally 中使用 Dispose() 调用创建一个 try/finally。为什么不给开发者统一的处理方式和异常处理方式呢?

回答by Chris Ballance

The using statement is actually changed into a try/finally block by the compiler in which the parameter of the using block is disposed of so long as it implements the IDisposable interface. Aside from ensuring the specified objects are properly disposed when they fall out of scope, there is really no error capturing gained by using this construct.

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

As is mentioned by TheSoftwareJediabove, you will want to make sure both the SqlConnection and SqlCommand objects are disposed of properly. Stacking both into a single using block is a bit messy, and might not do what you think it does.

正如上面TheSoftwareJedi所提到的,您需要确保 SqlConnection 和 SqlCommand 对象都被正确处理。将两者堆叠成一个 using 块有点混乱,并且可能不会像您认为的那样做。

Also, be mindful of using the try/catch block as logic. It's a code smell that my nose has a particular dislike for, and often used by newbies or those of us in a big hurry to meet a deadline.

另外,请注意使用 try/catch 块作为逻辑。这是一种代码气味,我的鼻子特别不喜欢,并且经常被新手或我们这些急于赶上最后期限的人使用。

回答by Charles Bretana

FYI, in this specific example, because you're using an ADO.net connection and Command object, be aware that the using statement just executes the Command.Dispose, and the Connection.Dispose() which do not actually close the connection, but simply releases it back into the ADO.net Connection pool to be reused by the next connection.open ... which is good, and the absolutely correct thing to do, bc if you don't, the connection will remain unuseable until the garbage collector releases it back to the pool, which might not be until numerous other connection requests, which would otherwise be forced to create new connections even though there's an unused one waiting to be garbage collected.

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

回答by Kevin Haines

Elaborating on what Chris Ballance said, the C# specification (ECMA-334 version 4) section 15.13 states "A using statement is translated into three parts: acquisition, usage, and disposal. Usage of the resource is implicitly enclosed in a try statement that includes a finally clause. This finally clause disposes of the resource. If a null resource is acquired, then no call to Dispose is made, and no exception is thrown."

详细说明 Chris Ballance 所说的内容,C# 规范(ECMA-334 第 4 版)第 15.13 节指出“using 语句被翻译成三部分:获取、使用和处置。资源的使用隐含在 try 语句中,其中包括finally 子句。这个 finally 子句处理资源。如果获取了空资源,则不会调用 Dispose,也不会抛出异常。”

The description is close to 2 pages - worth a read.

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

In my experience, SqlConnection/SqlCommand can generate errors in so many ways that you almost need to handle the exceptions thrown more than handle the expected behaviour. I'm not sure I'd want the using clause here, as I'd want to be able to handle the null resource case myself.

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

回答by Timothy Khouri

There are a lot of great answers here, but I don't think this has been said yet.

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

No matter what... the "Dispose" method WILL be called on the object in the "using" block. If you put a return statement, or throw an error, the "Dispose" will be called.

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

Example:

例子:

I made a class called "MyDisposable", and it implements IDisposable and simply does a Console.Write. It alwayswrites to the console even in all these scenarios:

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

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

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

回答by jop

If your code looks like this:

如果您的代码如下所示:

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

Then I would refactor it to use try.. catch.. finally instead.

然后我会重构它以使用 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();
}

In this scenario, I would be handling the exception so I have no choice but to add in that try..catch, I might as well put in the finally clause and save myself another nesting level. Note that I must be doing something in the catch block and not just ignoring the exception.

在这种情况下,我将处理异常,所以我别无选择,只能添加 try..catch,我不妨放入 finally 子句并为自己节省另一个嵌套级别。请注意,我必须在 catch 块中执行某些操作,而不仅仅是忽略异常。

回答by Andrew Kennan

If the caller of your function is responsible for dealing with any exceptions the using statement is a nice way of ensuring resources are cleaned up no matter the outcome.

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

It allows you to place exception handling code at layer/assembly boundaries and helps prevent other functions becoming too cluttered.

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

Of course, it really depends on the types of exceptions thrown by your code. Sometimes you should use try-catch-finally rather than a using statement. My habit is to always start with a using statement for IDisposables (or have classes that contain IDisposables also implement the interface) and add try-catch-finally as needed.

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