在Using语句中捕获异常

25

我知道 Using 语句会释放被创建的对象。例如,如果我想要这样做:

    Using(SqlConnection conn = new SqlConnection(connString))
    {
      //some code
      //How to show the users if conn is not opened up or generated some kind of error?
    }

如何在连接未打开或生成某种错误时向用户显示信息?


用TryCatch块包围。 - SimpleVar
你能否澄清一下你的问题?我不确定你在这里要求什么。 - Tejs
@Tejs:我需要使用“Using”语句向用户显示异常。 - RG-3
9个回答

26

using块中的代码并没有什么特别之处 - 只需使用try.catch来处理异常:

using(SqlConnection conn = new SqlConnection(connString))
{
    try
    {
        conn.Open();
        // do more stuff here......

    }
    catch(SqlException sqlEx)
    {
       // log error and possibly show to user in a MessageBox or something else
    }
}
using(...) { ... }块本身的设计只是为了确保当资源/对象不再需要时,它被正确释放。你无法通过using语句本身来处理错误。
因此,如果你期望创建对象可能失败,那么你必须将整个using块放在try ... catch块内,或者退回到try ... catch ... finally块并自行确保正确的释放(正如Adam在他的答案中建议的那样)。

5
在 using 语句中使用 try/catch 并不特别,甚至是良好的做法。释放对象和处理异常是不同的概念。 - Tim Schmelter
2
@Raj 因为 Try/Catch 是你捕获异常的方式。Using 只是一个 Try/Finally(没有 Catch)。如果你的问题是为什么它不在 Using 外面,那是因为创建连接不太可能导致错误,打开连接才会导致错误。 - Servy
1
@Raj为什么你不在using块内使用try/catch呢? - phoog
2
@Raj 因为那是它所属的位置。using 语句是 try/finally 块的简写形式,但不提供处理异常的机制。这段代码等同于 try/catch/finally,但在 finally 块中自动执行“正确的操作”。 - Preston Guillot
1
@marc_s 是的,在 using 块内部捕获异常是很明显的。但值得一提的是,异常可能会在 using 语句本身中发生。例如:构造函数 SqlConnection() 可能会传递无效的连接字符串或其他任何内容。 - user6694745
显示剩余2条评论

13

using并没有为catch提供任何后门。

只需手动展开它(在我看来,使用try/catch没有意义):

SqlConnection conn = null;

try
{
    conn = new SqlConnection("");
}
catch ...
{

}
finally
{
    if (conn != null)
        conn.Dispose();
}

我更喜欢这种方法,而不是将using包装在try-catch中或者大部分时间内将try-catch嵌入到using中,以避免编译后代码出现嵌套的try-catch。然而,如果你只需要覆盖一个大段代码中的非常小的子集,那么我会更加精细并将其嵌入。


@marc_s 语义学上的问题在于 using 不提供任何支持,你必须回到 try/catch。 - Adam Houldsworth
2
@marc_s 我的方式只编译一个try/catch,否则各自为政。我的方式提供的支持不亚于using - Adam Houldsworth

8
class SqlConnection
{
   using(sqlConnection)
   {

   }
}

class Consumer
{
   try
  {

  }
  catch(SqlException)
  {

  }

}

由类的使用者决定如何处理异常。


6

正如其他答案所述,只需添加普通的try/catch即可。

然而,我想补充一下,如果你的目标是“向用户显示”消息,那么这是放置try/catch的错误位置,特别是。让异常在这个级别发生,并允许其向上冒泡到更好地知道如何响应它的代码。

换句话说,保留你的代码示例。不要在该方法中添加任何新内容。但是,调用此方法的代码可能需要考虑如何处理来自数据库的异常...任何异常...。


你能给我展示一个例子吗?或者任何相关的链接都可以。谢谢! - RG-3
@Raj,请检查我的答案。那就是我在答案中提到的。 - Sandeep

4
以正常方式进行: 要么
try
{
    using(SqlConnection conn = new SqlConnection(connString)) 
    { 
      //some code        
    } 
}
catch (Exception exc)
{
    //handle error
}

或者

using(SqlConnection conn = new SqlConnection(connString)) 
{ 
    try
    {
        //some code 
    }
    catch (Exception exc)
    {
        //handle error
    }                
} 

2
@Widor:如果在“SqlConnection conn = new ...”处抛出异常,这个方法将无法处理。 - Ayush
嗯,这取决于您在其中做什么 - 也许您想捕获一个不相关于SQL的不同潜在异常。 - Widor

2
那只是没有使用它时的相同方式。
using(SqlConnection conn = new SqlConnection(connString))
{
  try{
    //some code
  }
  catch(SqlException e)
    MessageBox.Show(e.Message);
}

0

当您将using()块嵌套在try/catch中时,using()块确实会保证调用Dispose。但是,如果您的using块内部的非托管代码抛出异常,using()将只吞掉它,而不会到达catch块。使用try/catch在using()块内部,跳过using()并进行try/catch/finally,或者使用奇怪的"using() try"语法与catch块(这会让中级程序员感到困惑,因为它们会留下奇数个括号)。


0

你不要在 using 语句块内部执行它。

try
{
    using(SqlConnection conn = new SqlConnection(connString))
    {
        // Some code for when "conn" is succesfully instantiated
    }
}
catch (SomeSpecificConnectionInstantiationException ex)
{
    // Use ex to handle a bizarre instantiation exception?
}       

你可以在 using 语句块内部编写 try/catch。我相信! - RG-3
2
你可以不在using实例化中捕获异常。 - Jodrell
@Jodrell 但是一个 SqlConnection 通常不会在其构造函数中抛出异常。你可能遇到的唯一异常是参数表达式中的异常。 - Servy
1
这段代码不允许 catch 块访问生成异常的 SqlConnection 对象。 - Preston Guillot
@Jodrell - 不,这对于任何SqlConnection类型都是正确的,因为using语句也创建了一个作用域块。如果将try/catch放在using块外面,则无论类型如何,所创建的对象都不会可用于catch块。我希望看到Microsoft添加一个using/catch结构。 - Joel Coehoorn
显示剩余4条评论

-3

如果您想捕获使用块内代码抛出的异常,使用try{}catch(){}是一个好习惯。现在,请考虑以下两个示例——这解释了为什么在using语句中使用try-catch块是一个好习惯。

示例1

       try{
           using(SomeObject so = new SomeObject){
              // Perform some tasks
           }
       }catch(SomeException objSomeException){
             // Perform some actions, if exception occurs
       }

示例2

       using(SomeObject so = new SomeObject){
           try{    
               // Perform some tasks
              }catch(SomeException objSomeException){
                   // Perform some actions, if exception occurs
              }
       }

现在,如果在 using 语句内执行某些任务时发生异常,两个示例是否会产生相同的结果。简单的答案是否定的,原因是什么呢?

当在示例1中发生异常时,它被 catch 块捕获 - 而没有到达 using 块的末尾。因此,在示例1中的 someObject 将无法正确释放。即使 CLR 很慷慨(你不应该指望),示例1中使用的 someObject 的内存也不会被回收(或者最多会进入第二代 GC 集合)。

而在示例2中,catch 块位于 using 语句内部。这意味着执行将到达 using 块的末尾。因此,您的对象将被处理并且您不必担心内存泄漏(损坏)。


9
这是错误的。如果在使用过程中发生错误,对象将会被隐式的finally块释放,并且异常将由外部的catch块处理。在第二种情况下,异常将由内部的catch块处理,而using的隐式finally将稍后对其进行处理和释放。因此,唯一的区别在于第二个示例中的catch块仍然可以访问该对象,因此您仍然可以使用它来处理异常。在第一个示例中,在catch块中不再可用该对象。没有其他差异。 - JotaBe

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