您在处理异常时遇到的一些最常见的错误是什么?
似乎异常处理可能是学习如何在.Net中做“正确”的最困难的事情之一。特别是考虑到Common programming mistakes for .NET developers to avoid?目前排名第一的答案与异常处理有关。
希望通过列出一些最常见的错误,我们都可以学会更好地处理异常。
答案 0 :(得分:44)
您在处理异常时遇到的一些最常见的错误是什么?
我能想到很多。
首先阅读我关于将异常分类为 vexing , boneheaded ,致命和外生的文章:
http://ericlippert.com/2008/09/10/vexing-exceptions/
一些常见错误:
处理隐藏代码中的错误的异常;不要处理一个有骨头的异常,修复bug以免它首先被抛出
安全错误:未安全模式失败
try
{
result = CheckPassword();
if (result == BadPassword) throw BadPasswordException();
}
catch(BadPasswordException ex) { ReportError(ex); return; }
catch(Exception ex) { LogException(ex); }
AccessUserData();
看看发生了什么?我们未能进入不安全模式。如果CheckPassword抛出NetworkDriverIsAllMessedUpException,那么无论密码是否正确,我们都会抓住它,记录它并访问用户的数据。未能达到安全模式;当你得到任何例外时,假设最坏的情况。
安全错误:直接或间接产生泄漏敏感信息的异常。
这不完全是关于处理代码中的异常,而是关于生成由恶意代码处理的异常。
有趣的故事。在.NET 1.0发送给客户之前,我们发现了一个错误,可以调用一个引发异常的方法“调用此方法的程序集没有权限来确定文件C:\ foo.txt的名称”。大。谢谢你让我知道。什么阻止所述程序集捕获异常并询问其消息以获取文件名?没有。我们在发货前修好了。
这是一个直接的问题。间接问题是我在LoadPicture
中在VBScript中实现的问题。它给出了不同的错误消息,具体取决于不正确的参数是目录,不是图片的文件还是不存在的文件。这意味着你可以将它用作一个非常慢的磁盘浏览器!通过尝试一大堆不同的东西,你可以逐步建立一个人的硬盘上的文件和目录的图片。应该设计例外,以便如果它们由不可靠的代码处理,那么该代码就不会从用于引发异常的任何内容中获取用户的私有信息。 (LoadPicture现在提供的帮助错误信息要少得多。)
安全和资源管理错误:不清理资源的处理程序资源泄漏等待发生。资源泄漏可被用作恶意部分信任代码的拒绝服务攻击,故意产生异常情况。
健壮性错误:除非处理特定的外部异常,否则处理程序必须假设程序状态混乱。对于最后的块,尤其如此。当你处理一个意外的异常时,它完全有可能,甚至可能在你的程序中出现了一些问题。您不知道您的子系统是否正常工作,如果它们正在运行,是否会调用它们会使情况变得更好或更糟。集中精力记录错误并尽可能保存用户数据,并尽可能干净地关闭。假设一切正常。
安全错误:需要撤消具有安全影响的临时全局状态突变 之前任何可能存在敌意的代码都可以运行。在最终阻止运行之前,恶意代码可以运行!有关详细信息,请参阅我的文章:
http://blogs.msdn.com/ericlippert/archive/2004/09/01/224064.aspx
答案 1 :(得分:25)
重新抛出这样的例外:
try
{
// some code here
}
catch(Exception ex)
{
// logging, etc
throw ex;
}
这会杀死堆栈跟踪,使得它的可用性大大降低。重新抛出的正确方法是这样的:
try
{
// some code here
}
catch(Exception ex)
{
// logging, etc
throw;
}
答案 2 :(得分:12)
在许多情况下捕获所有异常时,您应尝试捕获特定的异常:
try {
// Do something.
} catch (Exception exc) {
// Do something.
}
而不是:
try {
// Do something.
} catch (IOException exc) {
// Do something.
}
应该从最具体到最不重要的例外订购。
答案 3 :(得分:10)
用无意义的信息重新提出例外。
try
{
...
}
catch (Exception ex)
{
throw new Exception("An error ocurred when saving database changes").
}
你不会相信我经常看到这样的代码在生产中运行。
答案 4 :(得分:9)
没有人在谈论看到像这样的空挡块......
try{
//do something
}
catch(SQLException sqex){
// do nothing
}
也永远不要使用异常处理来创建备用方法流...
try{
//do something
}catch(SQLException sqex){
//do something else
}
答案 5 :(得分:7)
未在using
个对象上使用IDisposable
:
File myFile = File.Open("some file");
callSomeMethodWhichThrowsException(myFile);
myFile.Close();
myFile在调用myFile的终结器之前不会关闭(可能永远不会),因为在调用myFile.Close()
之前抛出了异常。
正确的方法是
using(File myFile = File.Open("some file"))
{
callSomeMethodWhichThrowsException(myFile);
}
这会被编译器翻译成:
File myFile = File.Open("some file");
try
{
callSomeMethodWhichThrowsException(myFile);
}
finally
{
if(myFile != null)
myFile.Dispose(); //Dispose() calls Close()
}
因此,即使面对异常,文件也会被关闭。
答案 6 :(得分:6)
忘记重新抛出捕获的异常时忘记设置内部异常
try
{
...
}
catch (IOException ioException)
{
throw new AppSpecificException("It was not possible to save exportation file.")
// instead of
throw new AppSpecificException("It was not possible to save exportation file.", ioException);
}
当我发布这个答案时,我忘了提到我们应该始终考虑何时包含内部异常,因为安全原因。正如Eric Lippert所指出的another answer for this topic,一些例外可以提供有关服务器实现细节的敏感信息。因此,如果将处理将处理异常的调用者不受信任,则包含内部异常信息不是一个好主意。
答案 7 :(得分:4)
空捕获:
//What's the point?
catch()
{}
重新抛出:
//Exceptions are for *adding* detail up the stack
catch (Exception ex)
{throw ex;}
答案 8 :(得分:3)
假设涉及许多场景的异常是特定的。一个真实的场景是一个Web应用程序,其中异常处理总是假设所有错误都是会话超时并记录并报告所有错误作为会话超时。
另一个例子:
try
{
Insert(data);
}
catch (SqlException e)
{
//oh this is a duplicate row, lets change to update
Update(data);
}
答案 9 :(得分:3)
记录Exception.Message而不是Exception.ToString()
很多时候,我看到代码只记录异常消息,而它应该记录ToString方法的返回。 ToString提供有关异常的更多信息,而不是Message。除了消息之外,它还包括内部异常和堆栈跟踪等信息。
答案 10 :(得分:1)
尝试捕获OutOfMemoryException或StackOverflowException - 这些会导致运行时关闭,从而在同一个进程内(甚至从整个CLR中捕获它们)?< / p>
OutOfMemoryException:当没有足够的内存来继续执行程序时抛出的异常。
“从.NET Framework 2.0版开始,tryO catch块无法捕获StackOverflowException对象,默认情况下会终止相应的进程。因此,建议用户编写代码以检测并防止堆栈溢出。“
答案 11 :(得分:1)
无法在catch处理程序中捕获可能的异常。这可能导致错误的异常向上传播。
例如:
try
{
DoImportantWork();
}
catch
{
Cleanup();
throw;
}
如果Cleanup()
抛出异常会怎样?您不希望在此catch处理程序中看到指向Cleanup()方法的Exception。你想要原始的错误。您可以尝试记录清理错误,但即使您的日志记录代码也需要异常处理以避免从中抛出异常。
try
{
DoImportantWork();
}
catch
{
try
{
Cleanup();
}
catch
{
// We did our best to clean up, and even that failed.
// If you try to log this error, the logging may throw yet another Exception.
}
throw;
}
答案 12 :(得分:0)
<强>错误强>
try
{
// Do something stupid
}
catch
{
// ignore the resulting error because I'm lazy, and later spend
// a week trying to figure out why my app is crashing all over
// the place.
}
<强>更好强>
try
{
/// do something silly.
}
catch (InvalidOperationException ex)
{
/// respond, or log it.
}
catch (Exception e)
{
/// log it.
}
答案 13 :(得分:0)
使用异常进行正常流量控制。例外情况应该例外。如果这是一个好的/预期的操作,请使用返回值等