C# 如何使用 try catch 进行异常处理是最佳实践
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/14973642/
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
How using try catch for exception handling is best practice
提问by Toan Nguyen
while maintaining my colleague's code from even someone who claims to be a senior developer, I often see the following code:
在维护我同事的代码时,即使是自称是高级开发人员的人,我也经常看到以下代码:
try
{
//do something
}
catch
{
//Do nothing
}
or sometimes they write logging information to log files like following try catch
block
或者有时他们将日志信息写入日志文件,如以下try catch
块
try
{
//do some work
}
catch(Exception exception)
{
WriteException2LogFile(exception);
}
I am just wondering if what they have done is the best practice? It makes me confused because in my thinking users should know what happens with the system.
我只是想知道他们所做的是否是最佳实践?这让我感到困惑,因为在我看来,用户应该知道系统会发生什么。
Please give me some advice.
请给我一些建议。
采纳答案by Larry
My exception handling strategy is :
我的异常处理策略是:
To catch all unhandled exceptionsby hooking to the
Application.ThreadException event
, then decide :- For a UI application: to pop it to the user with an apology message (winforms)
- For a Service or a Console application: log it to a file (service or console)
要通过挂钩到来捕获所有未处理的异常
Application.ThreadException event
,然后决定:- 对于 UI 应用程序:使用道歉消息将其弹出给用户(winforms)
- 对于服务或控制台应用程序:将其记录到文件(服务或控制台)
Then I always enclose every piece of code that is run externallyin try/catch
:
然后,我始终将每一件在外部运行代码在try/catch
:
- All events fired by the Winforms infrastructure (Load, Click, SelectedChanged...)
- All events fired by third party components
- Winforms 基础结构触发的所有事件(Load、Click、SelectedChanged...)
- 第三方组件触发的所有事件
Then I enclose in 'try/catch'
然后我附上'try/catch'
- All the operations that I know might not work all the time(IO operations, calculations with a potential zero division...). In such a case, I throw a new
ApplicationException("custom message", innerException)
to keep track of what really happened
- 我所知道的所有操作可能不会一直有效(IO 操作、潜在零除法计算......)。在这种情况下,我会抛出一个新的
ApplicationException("custom message", innerException)
来跟踪真正发生的事情
Additionally, I try my best to sort exceptions correctly. There are exceptions which:
此外,我会尽力正确地对异常进行排序。有以下例外情况:
- need to be shown to the user immediately
- require some extra processing to put things together when they happen to avoid cascading problems (ie: put .EndUpdate in the
finally
section during aTreeView
fill) the user does not care, but it is important to know what happened. So I always log them:
- In the event log
- or in a .log file on the disk
- 需要立即显示给用户
- 需要一些额外的处理来在它们碰巧避免级联问题时将它们放在一起(即:
finally
在TreeView
填充期间将 .EndUpdate 放在该部分中) 用户并不关心,但重要的是要知道发生了什么。所以我总是记录它们:
- 在事件日志中
- 或在磁盘上的 .log 文件中
It is a good practice to design some static methods to handle exceptionsin the application top level error handlers.
设计一些静态方法来处理应用程序顶级错误处理程序中的异常是一种很好的做法。
I also force myself to try to:
我也强迫自己尝试:
- Remember ALL exceptions are bubbled up to the top level. It is not necessary to put exception handlers everywhere.
- Reusable or deep called functions does not need to display or log exceptions : they are either bubbled up automatically or rethrown with some custom messages in my exception handlers.
- 记住所有异常都冒泡到顶层。没有必要在任何地方放置异常处理程序。
- 可重用或深度调用的函数不需要显示或记录异常:它们要么自动冒泡,要么在我的异常处理程序中与一些自定义消息一起重新抛出。
So finally :
所以最后:
Bad:
坏的:
// DON'T DO THIS, ITS BAD
try
{
...
}
catch
{
// only air...
}
Useless:
无用:
// DONT'T DO THIS, ITS USELESS
try
{
...
}
catch(Exception ex)
{
throw ex;
}
Having a try finally without a catch is perfectly valid:
在没有捕获的情况下尝试最终是完全有效的:
try
{
listView1.BeginUpdate();
// If an exception occurs in the following code, then the finally will be executed
// and the exception will be thrown
...
}
finally
{
// I WANT THIS CODE TO RUN EVENTUALLY REGARDLESS AN EXCEPTION OCCURED OR NOT
listView1.EndUpdate();
}
What I do at the top level:
我在顶层做什么:
// i.e When the user clicks on a button
try
{
...
}
catch(Exception ex)
{
ex.Log(); // Log exception
-- OR --
ex.Log().Display(); // Log exception, then show it to the user with apologies...
}
What I do in some called functions:
我在一些所谓的函数中做什么:
// Calculation module
try
{
...
}
catch(Exception ex)
{
// Add useful information to the exception
throw new ApplicationException("Something wrong happened in the calculation module :", ex);
}
// IO module
try
{
...
}
catch(Exception ex)
{
throw new ApplicationException(string.Format("I cannot write the file {0} to {1}", fileName, directoryName), ex);
}
There is a lot to do with exception handling (Custom Exceptions) but thoses rules I try to keep in mind are enough for the simple applications I do.
异常处理(自定义异常)有很多事情要做,但是我试图记住的那些规则对于我所做的简单应用程序来说已经足够了。
Here is an example of extensions methods to handle caught exceptions a comfortable way. They are implemented a way they can be chained together, and it is very easy to add your own caught exception processing.
下面是一个扩展方法的例子,它以一种舒适的方式处理捕获的异常。它们以一种可以链接在一起的方式实现,并且很容易添加您自己的捕获异常处理。
// Usage:
try
{
// boom
}
catch(Exception ex)
{
// Only log exception
ex.Log();
-- OR --
// Only display exception
ex.Display();
-- OR --
// Log, then display exception
ex.Log().Display();
-- OR --
// Add some user-friendly message to an exception
new ApplicationException("Unable to calculate !", ex).Log().Display();
}
// Extension methods
internal static Exception Log(this Exception ex)
{
File.AppendAllText("CaughtExceptions" + DateTime.Now.ToString("yyyy-MM-dd") + ".log", DateTime.Now.ToString("HH:mm:ss") + ": " + ex.Message + "\n" + ex.ToString() + "\n");
return ex;
}
internal static Exception Display(this Exception ex, string msg = null, MessageBoxImage img = MessageBoxImage.Error)
{
MessageBox.Show(msg ?? ex.Message, "", MessageBoxButton.OK, img);
return ex;
}
回答by Pranay Rana
The second approach is a good one.
第二种方法很好。
If you don't want to show the error and confuse the user of application by showing runtime exception(i.e. error) which is not related to them, then just log error and the technical team can look for the issue and resolve it.
如果您不想通过显示与应用程序无关的运行时异常(即错误)来显示错误并迷惑应用程序的用户,那么只需记录错误,技术团队就可以查找问题并解决它。
try
{
//do some work
}
catch(Exception exception)
{
WriteException2LogFile(exception);//it will write the or log the error in a text file
}
I recommend that you go for the second approach for your whole application.
我建议您为整个应用程序采用第二种方法。
回答by Gregory Nozik
Best practice is to throw an Exception when the error occurs. Because an error has occurred and it should not be hidden.
最佳实践是在发生错误时抛出异常。因为发生了错误,不应将其隐藏。
But in real life you can have several situations when you want to hide this
但在现实生活中,当你想隐藏它时,你可能会遇到几种情况
- You rely on third party component and you want to continue the program in case of error.
- You have a business case that you need to continue in case of error
- 您依赖第三方组件,并且希望在出现错误时继续执行程序。
- 您有一个业务案例,如果出现错误,您需要继续
回答by Stasel
Leave blank catch block is the worse thing to do. If there is an error the best way to handle it is to:
留下空白的 catch 块是更糟糕的事情。如果出现错误,最好的处理方法是:
- Log it into file\database etc..
- Try to fix it on the fly (maybe trying alternative way of doing that operation)
- If we cannot fix that, notify the user that there is some error and of course abort the operation
- 将其登录到文件\数据库等中。
- 尝试即时修复它(也许尝试其他方法来执行该操作)
- 如果我们不能解决这个问题,通知用户有一些错误,当然中止操作
回答by fhnaseer
The better approach is the second one (the one in which you specify the exception type). The advantage of this is that you know that this type of exception can occur in your code. You are handling this type of exception and you can resume. If any other exception came, then that means something is wrong which will help you find bugs in your code. The application will eventually crash, but you will come to know that there is something you missed (bug) which needs to be fixed.
更好的方法是第二种方法(您指定异常类型的方法)。这样做的好处是您知道这种类型的异常可能会出现在您的代码中。您正在处理这种类型的异常,您可以继续。如果出现任何其他异常,则意味着有问题,这将帮助您找到代码中的错误。应用程序最终会崩溃,但您会知道您错过了一些需要修复的东西(错误)。
回答by Fanda
Sometimes you need to treat exceptions which say nothing to users.
有时您需要处理对用户毫无意义的异常。
My way is:
我的方法是:
- To catch uncaughted exceptions on application level (ie. in global.asax) for critical exceptions (application can not be useful). These exeptions I am not catching on the place. Just log them on app level and let system do its job.
- Catch "on place" and show some useful info to user (entered wrong number, can't parse).
- Catch on place and do nothing on marginal problems like "I will check for update info on the background, but the service is not running".
- 在应用程序级别(即在 global.asax 中)捕获未捕获的异常以获取关键异常(应用程序无法使用)。这些例外我没有抓住这个地方。只需在应用程序级别登录它们,让系统完成它的工作。
- 捕捉“就地”并向用户显示一些有用的信息(输入错误的数字,无法解析)。
- 赶上现场,对诸如“我将在后台检查更新信息,但服务未运行”等边缘问题不做任何处理。
It definitely does not have to be best practice. ;-)
它绝对不必是最佳实践。;-)
回答by Thai Anh Duc
To me, handling exception can be seen as business rule. Obviously, the first approach is unacceptable. The second one is better one and it might be 100% correct way IF the context says so. Now, for example, you are developing an Outlook Addin. If you addin throws unhandled exception, the outlook user might now know it since the outlook will not destroy itself because of one plugin failed. And you have hard time to figure out what went wrong. Therefore, the second approach in this case, to me, it is a correct one. Beside logging the exception, you might decide to display error message to user - i consider it as a business rule.
对我来说,处理异常可以看作是业务规则。显然,第一种方法是不可接受的。第二个更好,如果上下文如此,它可能是 100% 正确的方式。例如,现在您正在开发 Outlook 插件。如果您的插件抛出未处理的异常,Outlook 用户现在可能知道它,因为 Outlook 不会因为一个插件失败而自行销毁。而且你很难弄清楚哪里出了问题。因此,在这种情况下,第二种方法对我来说是正确的。除了记录异常之外,您还可能决定向用户显示错误消息 - 我认为这是一项业务规则。
回答by Matías Fidemraizer
An exception is a blocking error.
一个例外是阻塞错误。
First of all, the best practice should be don't throw exceptions for any kind of error, unless it's a blocking error.
首先,最佳实践应该是不要为任何类型的错误抛出异常,除非它是一个阻塞错误。
If the error is blocking, then throw the exception. Once the exception is already thrown, there's no need to hide it because it's exceptional; let the user know about it (you should reformat the whole exception to something useful to the user in the UI).
如果错误是阻塞,则抛出异常。一旦异常已经抛出,就没有必要隐藏它,因为它是异常的;让用户知道它(您应该在 UI 中将整个异常重新格式化为对用户有用的内容)。
Your job as software developer is to endeavour to prevent an exceptional casewhere some parameter or runtime situation may end in an exception. That is, exceptions mustn't be muted, but these must be avoided.
您作为软件开发人员的工作是努力防止特殊情况,其中一些参数或运行时情况可能异常结束。也就是说,不能忽略异常,但必须避免这些异常。
For example, if you know that some integerinput could come with an invalid format, use int.TryParse
instead of int.Parse
. There is a lot of cases where you can do this instead of just saying "if it fails, simply throw an exception".
例如,如果您知道某些整数输入可能带有无效格式,请使用int.TryParse
代替int.Parse
。在很多情况下,您可以执行此操作,而不仅仅是说“如果失败,只需抛出异常”。
Throwing exceptions is expensive.
抛出异常的代价很高。
If, after all, an exception is thrown, instead of writing the exception to the log once it has been thrown, one of best practices is catching it in a first-chance exception handler. For example:
毕竟,如果抛出了异常,与其在抛出异常后将异常写入日志,最好的做法之一是在第一次机会异常处理程序中捕获它。例如:
- ASP.NET: Global.asax Application_Error
- Others: AppDomain.FirstChanceException event.
- ASP.NET: Global.asax Application_Error
- 其他:AppDomain.FirstChanceException 事件。
My stance is that local try/catches are better suited for handling special cases where you may translate an exception into another, or when you want to "mute" it for a very, very, very, very, very special case (a library bug throwing an unrelated exception that you need to mute in order to workaround the whole bug).
我的立场是,本地 try/catch 更适合处理特殊情况,在这种情况下,您可能会将异常转换为另一个异常,或者当您想在非常、非常、非常、非常、非常非常特殊的情况下(库错误)将其“静音”抛出一个不相关的异常,您需要将其静音以解决整个错误)。
For the rest of the cases:
对于其余情况:
- Try to avoid exceptions.
- If this isn't possible: first-chance exception handlers.
- Or use a PostSharp aspect (AOP).
- 尽量避免异常。
- 如果这是不可能的:第一机会异常处理程序。
- 或者使用 PostSharp 方面 (AOP)。
Answering to @thewhiteambit on some comment...
在一些评论上回答@thewhiteambit...
@thewhiteambit said:
@thewhiteambit 说:
Exceptions are not Fatal-Errors, they are Exceptions! Sometimes they are not even Errors, but to consider them Fatal-Errors is completely false understanding of what Exceptions are.
异常不是致命错误,它们是异常!有时它们甚至不是错误,但将它们视为致命错误是对异常是什么的完全错误的理解。
First of all, how an exception can't be even an error?
首先,异常怎么就不可能是错误呢?
- No database connection => exception.
- Invalid string format to parse to some type => exception
- Trying to parse JSON and while input isn't actually JSON => exception
- Argument
null
while object was expected => exception - Some library has a bug => throws an unexpected exception
- There's a socket connection and it gets disconnected. Then you try to send a message => exception
- ...
- 没有数据库连接 => 异常。
- 解析为某种类型的字符串格式无效 => 异常
- 试图解析 JSON 而输入实际上并不是 JSON => 异常
null
预期对象时的参数=> 异常- 某些库有错误 => 引发意外异常
- 有一个套接字连接,它断开连接。然后您尝试发送消息 => 异常
- ...
We might list 1k cases of when an exception is thrown, and after all, any of the possible cases will be an error.
我们可能会列出 1k 种抛出异常的情况,毕竟,任何可能的情况都将是错误。
An exception isan error, because at the end of the day it is an object which collects diagnostic information -- it has a message and it happens when something goes wrong.
异常是错误,因为归根结底它是一个收集诊断信息的对象——它有一条消息,当出现问题时就会发生。
No one would throw an exception when there's no exceptional case. Exceptions should be blocking errorsbecause once they're thrown, if you don't try to fall into the use try/catch and exceptions to implement control flowthey mean your application/service will stop the operation that entered into an exceptional case.
当没有异常情况时,没有人会抛出异常。异常应该是阻塞错误,因为一旦它们被抛出,如果你不尝试使用 try/catch 和异常来实现控制流,它们意味着你的应用程序/服务将停止进入异常情况的操作。
Also, I suggest everyone to check the fail-fastparadigm published by Martin Fowler (and written by Jim Shore). This is how I always understood how to handle exceptions, even before I got to this document some time ago.
另外,我建议大家检查一下由 Martin Fowler 发布(由 Jim Shore 编写)的fail-fast范式。这就是我一直理解如何处理异常的方式,甚至在我前段时间阅读本文档之前。
[...] consider them Fatal-Errors is completely false understanding of what exceptions are.
[...] 认为它们致命错误是对异常是什么的完全错误的理解。
Usually exceptions cutsome operation flow and they're handled to convert them to human-understandable errors. Thus, it seems like an exception actually is a better paradigm to handle error cases and work on them to avoid an application/service complete crash and notify the user/consumer that something went wrong.
通常异常会减少一些操作流程,并且会处理它们以将它们转换为人类可以理解的错误。因此,似乎异常实际上是处理错误情况并对其进行处理以避免应用程序/服务完全崩溃并通知用户/消费者出现问题的更好范例。
More answers about @thewhiteambit concerns
有关@thewhiteambit 问题的更多答案
For example in case of a missing Database-Connection the program could exceptionally continue with writing to a local file and send the changes to the Database once it is available again. Your invalid String-To-Number casting could be tried to parse again with language-local interpretation on Exception, like as you try default English language to Parse("1,5") fails and you try it with German interpretation again which is completely fine because we use comma instead of point as separator. You see these Exceptions must not even be blocking, they only need some Exception-handling.
例如,在缺少数据库连接的情况下,程序可以异常地继续写入本地文件,并在数据库再次可用时将更改发送到数据库。您可以尝试使用本地语言解释对 Exception 再次解析无效的 String-To-Number 转换,就像您尝试将默认英语语言解析为 Parse("1,5") 失败,然后您再次尝试使用德语解释,这完全是很好,因为我们使用逗号而不是点作为分隔符。你会看到这些异常甚至不能阻塞,它们只需要一些异常处理。
If your app might work offline without persisting data to database, you shouldn't use exceptions, as implementing control flow using
try/catch
is considered as an anti-pattern. Offline work is a possible use case, so you implement control flow to check if database is accessible or not, you don't wait until it's unreachable.The parsingthing is also an expected case (not EXCEPTIONAL CASE). If you expect this, you don't use exceptions to do control flow!. You get some metadata from the user to know what his/her culture is and you use formatters for this! .NET supports this and other environments too, and an exception because number formatting must be avoided if you expect a culture-specific usage of your application/service.
如果您的应用程序可能脱机工作而不将数据持久化到数据库,则不应使用exceptions,因为使用实现控制流
try/catch
被视为反模式。离线工作是一个可能的用例,因此您可以实施控制流来检查数据库是否可访问,而不是等到无法访问。该分析事情也是预期的情况下(没有特殊情况)。如果您期望这一点,则不会使用异常来进行控制流!. 你从用户那里得到一些元数据来了解他/她的文化是什么,你为此使用格式化程序!.NET 也支持这种环境和其他环境,但有一个例外,因为如果您希望应用程序/服务具有特定于文化的用法,则必须避免数字格式。
An unhandled Exception usually becomes an Error, but Exceptions itself are not codeproject.com/Articles/15921/Not-All-Exceptions-Are-Errors
未处理的异常通常会变成错误,但异常本身并不是 codeproject.com/Articles/15921/Not-All-Exceptions-Are-Errors
This article is just an opinion or a point of view of the author.
这篇文章只是作者的一个意见或观点。
Since Wikipedia can be also just the opinion of articule author(s), I wouldn't say it's the dogma, but check what Coding by exceptionarticle says somewhere in some paragraph:
由于维基百科也可能只是文章作者的意见,我不会说这是教条,而是检查编码异常文章在某些段落的某处说了什么:
[...] Using these exceptions to handle specific errors that arise to continue the program is called coding by exception. This anti-pattern can quickly degrade software in performance and maintainability.
[...] 使用这些异常来处理为继续程序而出现的特定错误称为异常编码。这种反模式会迅速降低软件的性能和可维护性。
It also says somewhere:
它还在某处说:
Incorrect exception usage
异常使用不正确
Often coding by exception can lead to further issues in the software with incorrect exception usage. In addition to using exception handling for a unique problem, incorrect exception usage takes this further by executing code even after the exception is raised. This poor programming method resembles the goto method in many software languages but only occurs after a problem in the software is detected.
通常,按异常编码会导致软件中出现更多问题,异常使用不正确。除了对一个独特的问题使用异常处理之外,不正确的异常使用甚至在引发异常之后通过执行代码来进一步实现这一点。这种糟糕的编程方法类似于许多软件语言中的 goto 方法,但仅在检测到软件中的问题后才会发生。
Honestly, I believe that software can't be developed don't taking use cases seriously. If you know that...
老实说,我认为不认真对待用例就无法开发软件。如果你知道那...
- Your database can go offline...
- Some file can be locked...
- Some formatting might be not supported...
- Some domain validation might fail...
- Your app should work in offline mode...
- whatever use case...
- 您的数据库可以脱机...
- 某些文件可以被锁定...
- 某些格式可能不受支持...
- 某些域验证可能会失败...
- 您的应用应该在离线模式下工作...
- 无论用例...
...you won't use exceptions for that. You would supportthese use cases using regular control flow.
...您不会为此使用异常。您将使用常规控制流支持这些用例。
And if some unexpected use case isn't covered, your code will fail fast, because it'll throw an exception. Right, because an exception is an exceptional case.
如果未涵盖某些意外用例,您的代码将很快失败,因为它会抛出异常。是的,因为异常就是特例。
In the other hand, and finally, sometimes you cover exceptional casesthrowing expected exceptions, but you don't throw them to implement control flow. You do it because you want to notify upper layers that you don't support some use case or your code fails to work with some given arguments or environment data/properties.
在另一方面,最后,有时你覆盖特殊情况下抛出预期的异常,但你不把它们实施控制流。您这样做是因为您想通知上层您不支持某些用例,或者您的代码无法使用某些给定的参数或环境数据/属性。
回答by Anirudha
The catch
without any arguments is simply eatingthe exception and is of no use. What if a fatal error occurs? There's no way to know what happened if you use catch without argument.
在catch
不带任何参数仅仅是吃的例外,是没有用的。如果发生致命错误怎么办?如果不加参数地使用 catch ,就无法知道发生了什么。
A catch statement should catch more specificExceptions like FileNotFoundException
and then at the very endyou should catch Exception
which would catch any other exception and log them.
catch 语句应该捕获更具体的异常FileNotFoundException
,然后在最后你应该捕获Exception
它会捕获任何其他异常并记录它们。
回答by Sorcerer86pt
With Exceptions, I try the following:
有例外,我尝试以下操作:
First, I catch special types of exceptions like division by zero, IO operations, and so on and write code according to that. For example, a division by zero, depending the provenience of the values I could alert the user (example a simple calculator in that in a middle calculation (not the arguments) arrives in a division by zero) or to silently treat that exception, logging it and continue processing.
首先,我捕获特殊类型的异常,例如被零除、IO 操作等,并根据这些异常编写代码。例如,除以零,取决于值的出处,我可以提醒用户(例如一个简单的计算器,在中间计算(不是参数)到达除以零)或静默处理该异常,记录它并继续处理。
Then I try to catch the remaining exceptions and log them. If possible allow the execution of code, otherwise alert the user that a error happened and ask them to mail a error report.
然后我尝试捕获剩余的异常并记录它们。如果可能,允许执行代码,否则提醒用户发生错误并要求他们邮寄错误报告。
In code, something like this:
在代码中,是这样的:
try{
//Some code here
}
catch(DivideByZeroException dz){
AlerUserDivideByZerohappened();
}
catch(Exception e){
treatGeneralException(e);
}
finally{
//if a IO operation here i close the hanging handlers for example
}