捕获(Exception)和捕获(Exception ex)之间的区别

10

Catch(Exception)Catch(Exception ex)有什么区别?我看到两者都可以给出预期的输出。那么实际上有什么区别呢?哪一个更推荐使用?

假设代码如下。

int a = 1, b = 0;
try
{
    int c = a / b;
    Console.WriteLine(c);
}

哪种下面的catch块建议使用?它们之间的实际区别是什么?

catch (Exception ex)
{
    Console.WriteLine(ex.Message);
}

或者

catch (Exception)
{
    Console.WriteLine("Oh NO!!");
}

1
如果你对捕获的异常信息(堆栈信息、消息等)不感兴趣,你可以简单地写成 catch {Console.WriteLine("Oh No");} - Steve
1
区别在于,你会对第一个片段中程序停止正常工作有一些想法,而在第二个片段中则完全没有。第二种方式是导致SO用户问出“它不起作用,出现了错误”的问题的原因。根本不要编写这样的代码。 - Hans Passant
3
@Steve,catchcatch (Exception)之间有微小的差别。前者将捕获所有异常对象,而后者仅捕获从Exception派生的异常对象。在C#中,您只能抛出从Exception派生的对象,但CLR支持更多。 - user1908061
6个回答

6

其实,catch(Exception ex)catch(Exception) 是一样的,唯一的不同就是:在 catch(Exception ex) 中,我们可以访问异常类(错误原因)的实例。通常需要一个异常类实例来打印出原始信息:

  try {
    ...
  }
  catch (AppServerException e) {
    Console.WriteLine("Application server failed to get data with the message:");
    Console.WriteLine(e.Message); // <- What's actually got wrong with it
  }

如果您不需要异常类的实例,比如您只打算消耗异常,那么catch(Exception ex)语法是多余的,而catch(Exception)则更可取。
  try {  
    c = a / b;
  }  
  catch (DivideByZeroException) {
    c = Int.MaxValue; // <- in case b = 0, let c be the maximum possible int
  }

最后,不要捕获一般的异常类而不重新抛出:
  try {
    int c = a / b;
  }
  catch (Exception) { // <- Never ever do this!
    Console.WriteLine("Oh NO!!");
  }

你真的想编写“无论发生什么错误(包括来自CPU的绿色烟雾),都只需打印“Oh No”并继续”吗?使用Exception类的模式如下:

  tran.Start();

  try {
    ...
    tran.Commit();
  }
  catch (Exception) {
    // Whatever had happened, let's first rollback the database transaction
    tran.Rollback();

    Console.WriteLine("Oh NO!");

    throw; // <- re-throw the exception
  }

2
我不同意“不要捕获一般的异常类而不重新抛出”的说法。以及“你真的想编写 '无论什么错误' 只打印 '哦,不' 并继续吗?”这种说法。1.有时候我确实会这样做。2.还有其他事情我可能想做(除了记录日志)。在代码中捕获 Exception ex 是很常见的。 - tymtam
据我所知,应该是 DivideByZeroException 而不是 ZeroDivisionException - tymtam
@DmitryBychenko 先生,throw; 这一行的目的是什么?我猜它用于 throw 异常,但为什么我们要这样做呢?这样做有什么好处?我看到很多 catch 语句,在 catch 的末尾没有 throw。谢谢。 - Roxy'Pro
@Roxy'Pro:throw; 会重新抛出异常;请注意,在上一个示例中,我们部分地处理了异常——我们回滚了事务并显示了消息。但是,它可能需要一些其他操作,这就是为什么我们重新抛出异常,以便高级别例程知道低级别操作失败的原因。 - Dmitry Bychenko
@DmitryBychenko 你的意思是我们抛出异常是因为某个更高级别的异常处理程序可能会执行一些其他操作吗?我真的很困惑为什么在catch块中使用throw,对我来说,始终只需要记录异常就可以了...但许多人在catch块的末尾放置throw,我无法理解为什么。 - Roxy'Pro
@Roxy'Pro:是的,可能非常需要额外的操作。想象一下,你想将数据导出到数据库,如果一切正常,你就提交更改,比如说,csv文件被删除了。如果不行,你应该回滚更改(这应该在低级别代码中完成);然而这还不够:我们应该保留csv文件不变(这是高级别代码的功能),并(根据某些设置)发送电子邮件。在这里,我们完成了我们部分的异常处理(无论如何事务都应该回滚),并让同样的异常被更高层次的处理。 - Dmitry Bychenko

3

如果您需要在catch块内使用异常,请给Exception命名; 否则,保持匿名。

有时候catch块需要执行更多操作,而不仅仅是显示异常消息。例如,在捕获特定于应用程序的异常时,您可能能够检查异常对象的其他元素。以下是一个假设的示例:

catch (ConnectToServerException cse) {
    MessageBox.Show(string.Format(
        "Connection to server '{0}' failed. Use a name from the following list: {1}"
    ,   cse.AttemptedConnectionServerName
    ,   string.Join(", ", cse.AllowedServerNames)
    ));
}

上述代码假设自定义异常 ConnectToServerException 具有一个名为 AttemptedConnectionServerName 的属性,其中包含您尝试连接的服务器名称,并具有一个枚举属性 AllowedServerNames,其中包含可用服务器的名称。
此外,还有一些情况只需知道特定类型的异常已发生。在这种情况下,您无需为其提供命名变量。

3

非常简单:

  • 在第一段代码中,您可以捕获异常并获取代表它的对象,以便您可以获得有关发生了什么的更多信息
  • 在第二段代码中,您只知道引发了异常,但没有更多关于它的信息。

实际上使用哪个取决于您想要获取有关引发的异常的信息量。


0

区别在于一个会打印尝试除以零。而另一个会打印哦不!!

处理异常是一个复杂的问题,取决于应用程序,但以下是一些一般性的评论:


通常最好为特定的异常提供处理程序:
类似这样的代码:
 catch ({System.DivideByZeroException ex )
 {
    Console.WriteLine("Ops. I cannot divide by zero." );
 }
 catch ({System.Exception ex )
 {
    Console.WriteLine("There was an error during calculations: {0}", ex.Message );
 }

迟早你会发现Console.WriteLine并不够好,你将不得不使用一个记录器,所以最好早点开始使用它。


理想情况下,如果您决定向用户公开原始错误消息,您应该打印异常链中的所有消息,或者至少是最深层的消息。
类似这样的内容:
 catch ({System.DivideByZeroException ex )
 {
    Console.WriteLine("Oops. I cannot divide by zero." );
 }
 catch ({System.Exception ex )
 {
    Console.WriteLine(GetExceptionMsgs(ex));
 }

 ...in another class...
public static string GetExceptionMsgs(Exception ex) {
   if( ex == null ) {
       return "No exception = no details";
   }
   var sb = new StringBuilder();
   while( ex != null ) {
        sb.AppendLine(ex.Message);
        ex = ex.InnerException;
   }
   return sb.ToString()
}

0

Catch (Exception) 做的事情与 Exception 类型相同。

Catch (Exception ex) 捕获所有异常,并且您可以通过其引用检索消息。

使用取决于要求,如果您想显示异常消息,则可以使用 ex.Message,否则 Catch (Exception) 就足够了。


0

使用 catch(Exception) 只需要在 catch 块中指定要处理的异常类型(在本例中,任何引发的异常都是一样的,因此与仅使用 catch 相同)。

使用 catch(Exception ex) 会传递实际引发的异常实例,因此您可以访问异常的属性并根据提供的信息执行某些操作。


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