在C#中,'Catch'、'Catch (Exception)'和'Catch(Exception e)'这三种catch块的变体有什么区别?

21
在C#中,“Catch”,“Catch(Exception)”和“Catch(Exception e)”有什么区别? MSDN文章在其示例中使用了其中的两个,但没有解释用途上的区别。
try
{}
catch 
{}

try 
{}
catch (Exception)
{}

try
{}
catch(Exception e)
{}

这些有什么不同?哪些可以捕获所有异常,哪些只能捕获特定的异常?

1
所有三个都捕获所有异常,区别在于第三个可以检索消息。 - Sayse
1
MSDN上的文档非常有帮助。http://msdn.microsoft.com/en-us/library/0yd65esw.aspx - Michael Gunter
1
第二个可以指定类型。 - SLaks
3
评分-1是因为研究不够充分。这篇文章可能是https://dev59.com/oWgv5IYBdhLWcg3wKtlB及其他无数篇文章的副本。 - Anthony Pegram
关闭并标记为“不是一个真正的问题”?也许是一个重复的问题,但它肯定是一个有效的问题。当您决定扮演上帝时,请确保向新用户发送正确的信息。 - DonBoitnott
显示剩余3条评论
7个回答

46

目前还没有人提到这个问题的历史方面。

在.NET中,抛出一个不派生自Exception的对象是合法的。(在C#中是不合法的,但在一些其他托管语言中是合法的。)许多人不知道这个事实,但它是合法的。由于这很疯狂,所以在.NET 2.0中更改了默认设置:如果您试图抛出的对象不是异常,则它会自动包装在RuntimeWrappedException类中,该类显然是一个异常。然后抛出该对象。

因为这种怪异的情况,在C# 1.0中常见的代码同时执行以下两个操作:

try
{ do something }
catch(Exception) 
{ handle the exception }
catch
{ handle the thrown non-exception }
事实上,存在安全性和正确性问题; 在某些情况下出于安全原因,您可能需要捕获任何被抛出的内容(可能会重新抛出),人们通常会认为catch(Exception)可以捕获所有内容,但事实并非如此。幸运的是,自.NET 2.0以来,情况变得更加明智了;您可以依靠catch {}catch(Exception) {}catch(Exception ex) {}来捕获所有必要的内容。最后:如果出于某种疯狂的原因,您想启用C# 1.0行为,可以执行以下操作:
[assembly:System.Runtime.CompilerServices.RuntimeCompatibility(WrapNonExceptionThrows = false)]

在你的程序中。


1
+1 是因为你的声望几乎为零。哦,还有历史课 :) - Brian
1
相关主题:如何抛出一个不继承自Exception的异常? - Jeppe Stig Nielsen
这对.NET Compact Framework 2.0版本及以后的版本也适用吗? - Thomas
@Thomas:这是一个问答网站。如果你有问题,请发布问题,有人会回答它。我注意到,“框架X是否支持特性Y?”是一个你可以通过尝试来回答的问题。 - Eric Lippert

24
简而言之...
没有参数的Catch语句可以捕获任何异常,但不能提供解决方法。 Catch (Exception)语句本质上与前者相同,因为您已经指定了根Exception类型。 而Catch (IOException)语句只会捕获IOException类型的异常。 Catch (Exception ex)语句可以捕获所有异常,并通过变量ex提供一种解决方法。
阅读更多:http://msdn.microsoft.com/en-us/library/ms173160.aspx

9

第一个版本捕获所有从Exception类派生的异常。

第二个版本捕获指定的异常。

第三个版本使用声明名称捕获指定的异常。然后在catch块中,您可以使用此对象,例如查看完整的错误:e.ToString();

阅读更多信息这里


实际上,您将使用 e.Message - DonBoitnott
1
@DonBoitnott:不,你不应该使用e.Message。那只是用户看到的“漂亮”消息。e.ToString()会显示所有信息,包括内部异常。 - John Saunders
@JohnSaunders 您是指“堆栈跟踪”吗?如果是的话,我同意。 - DonBoitnott
还要注意,这些版本捕获Exception类型或任何派生自Exception的内容。 - Adam Houldsworth
1
@DonBoitnott:不仅仅是e.StackTrace。ToString将显示内部异常、服务器端异常等。 - John Saunders

6
所有的语句基本上都是做同样的事情,它们之间的区别在于提供有关错误的信息量不同。 catch (foo ex) {} 将过滤掉所有异常,除了那些可以转换为类型 foo 的异常。它还会为您提供错误实例以便处理。 catch (foo) {} 与上面的语句相同,但不会为您提供异常实例。您将知道异常类型,但无法从中读取信息。
请注意,在这两种情况下,如果异常类型为 Exception,它们将捕获所有异常。 catch {} 捕获所有异常。您不知道它捕获的类型,并且无法访问实例。
您可以根据需要从捕获的异常中获取多少信息来选择使用哪个语句。
无论使用哪个语句,您都可以通过使用命令 throw;(不带参数)将捕获的异常传递给后续处理语句。

3
在你的例子中,由于你没有对异常进行任何处理,所以什么也不会发生。但为了澄清一下...
  • This catches everything but does nothing with the exception.

    catch {}
    

    This is only useful when you want to say guarantee a return from a method.

  • This catches only exceptions of type Exception (which is everything) but does nothing with the exception.

    catch (Exception) {}
    

    This is useful if you wanted to limit the type of exception being caught by specifying which types you want to handle. Any other exceptions will bubble up the call stack until a proper handler is found.

  • This catches only exceptions of type Exception (which is everything) and could do something with the exception, but happens to do nothing

    catch (Exception ex) {}
    

    This technique gives you a lot more options. You could log the exception, inspect the InnerException, etc. And again you can specify which types of exceptions you wish to handle.

除非你要重新抛出异常,否则所有这些做法都是不好的。通常来讲,你只应该捕获你可以有意义地处理的异常,并且允许其他任何异常继续传递。


该语言不允许在已经处理了此catch将要处理的每个类型的另一个catch块后面再出现catch块。但是,它允许catch (Exception) { ... } catch { ... },其中最后一个块可能无法到达。根据这个答案,除了C#之外的一些语言可能会"throw"一个不是System.Exception的对象。这是真的吗?我听说在这种情况下,异类对象最终仍将由运行时包装在Exception中,但这是否正确? - Jeppe Stig Nielsen
Lippert在他的回答中已经解释过了。 - Jeppe Stig Nielsen

1
添加 catch(Exception e) 会使您可以访问 Exception 对象,其中包含有关引发异常的详细信息,例如其 Message 和 StackTrace。记录此信息以帮助诊断错误非常有用。如果未声明尝试捕获的异常,简单的 catch 将无法访问异常对象。
此外,通常认为捕获基本 Exception 是不良实践,因为它太过于通用,如果可能,您应该始终先针对特定的异常进行类型转换。例如,如果您正在使用文件,则可以考虑以下 try/catch 块:
try{
    //open your file and read/write from it here
}catch(FileNotFoundException fe){
    //log the message
    Log(fe.Message);
}catch(Exception e){
    //you can catch a general exception at the end if you must
}finally{
    //close your file
}

1

在最高层面上,它们都是相同的;它们都捕获异常。

但是如果进一步深入,第一个例子中,您捕获了一个异常并且没有做任何处理(您没有定义类型)。在第二个例子中,您捕获了 Exception 类型的异常。在您最后的例子中,您捕获了和示例2中相同的 Exception 类型,但现在您将异常放入一个变量中,这样您可以在 MessageBox 或其他地方显示它:

e.Message

需要注意的是,异常是分层的。这意味着,如果你在同一个try/catch块中捕获多种类型的异常,你应该从最具体的异常类型到最一般的异常类型进行处理。就像这样:

try {
}
catch (SqlException sqlExc) {
}
catch (Exception exc) {
}

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