一、在哪里处理一般异常?
如果非主程序永远不处理你不知道怎么处理的异常(也就是不要遮掩异常),而是将异常向上传递。
除非你有下列的理由之一,否则不要捕获该异常。
(1)能够(需要)处理该异常
(2)能够忽略该异常
(3)需要转换该异常为其它特定异常后抛出新异常
二、、什么时候捕一般获异常
1、 除了在主程序,永远不要捕获一般异常(也就是Exception),
2、主程序应该捕获所有可能的异常(一般异常),捕获后可以
(1)将无关紧要的异常忽略。
(2)将异常转换为错误信息展现给用户。
(3)如果可能,对由于产生异常而没有正确执行的操作做补救措
(4)对于无法处理的重大异常,可以考虑终止应用程序。
3、主程序(特别是最上层的UI层)不应该抛出新的异常,否则该异常会导致应用程序终止(将异常直接呈现给用户,是十分不好的用户体验)
三、捕获和抛出异常
1、不要遮掩异常
非主程序不应该捕获所有异常 ,也不应该捕获主异常什么都不做。这样就隐藏了异常,也就隐藏了BUG。如代码A 和 代码B.
A:
1 | try |
2 | { |
3 | //Do something |
4 | |
5 | } |
6 | catch (Exception ex) |
7 | { |
8 | // handler exception only |
9 | } |
10 |
B:
1 | try |
2 | { |
3 | // Do something |
4 | |
5 | } |
6 | catch |
7 | { } |
8 |
2、非主程序只捕获特定的异常
对于一个处理xml流来说,我们应该只捕获需要处理的XmlException(代码B),而不是捕获所有可能的Exception(代码A)。因为A中
catch (Exception ex)捕获了所有可能的异常,也就遮掩的其他可能的异常。
A:
1 | try |
2 | { |
3 | using (XmlReader topicReader = XmlReader.Create (xmlStream, readersetting)) |
4 | { |
5 | // xml processing |
6 | } |
7 | } |
8 | catch (Exception ex) |
9 | { |
10 | // handler xml exception only |
11 | } |
12 |
B:
1 | try |
2 | { |
3 | using (XmlReader topicReader = XmlReader.Create (xmlStream, readersetting)) |
4 | { |
5 | // xml processing |
6 | } |
7 | } |
8 | catch (XmlException ex) |
9 | { |
10 | // handler xml exception only |
11 | } |
12 | |
13 |
3、对于发生异常仍然要正常执行的逻辑,应对每段可能发生异常的代码分别写try-catch块。
如在关闭某个窗体释放由该窗体所占的所有资源时,即使释放某个资源的过程中产生了异常也应该继续释放其他未释放的资源,这时就应该分别对每个释放资源的代码写try-catch块(如代码A)。
代码A,虽然看似不简洁,但当DeviceManager.ReleaseAll() 出现异常的时候,也不会影响MessageManager .Dispose() 所占资源的继续释放。而代码B:貌似简洁,一旦DeviceManager.ReleaseAll()发生异常MessageManager .Dispose()将无法执行。
A:
1 | private void ExitMainForm_Click(object sender, EventArgs e) |
2 | { |
3 | if (DeviceManager!= null) |
4 | { |
5 | try |
6 | { |
7 | DeviceManager.ReleaseAll(); |
8 | } |
9 | catch (Exception ex) |
10 | { |
11 | //write log |
12 | } |
13 | } |
14 | if (MessageManager != null) |
15 | { |
16 | try |
17 | { |
18 | MessageManager .Dispose(); |
19 | } |
20 | catch (Exception ex) |
21 | { |
22 | //write log |
23 | } |
24 | } |
25 | } |
26 |
B:
1 | private void ExitMainForm_Click(object sender, EventArgs e) |
2 | { |
3 | try |
4 | { |
5 | if (DeviceManager!= null) |
6 | { |
7 | DeviceManager.ReleaseAll(); |
8 | } |
9 | |
10 | if (MessageManager != null) |
11 | { |
12 | MessageManager .Dispose(); |
13 | } |
14 | } |
15 | catch (Exception ex) |
16 | { |
17 | //write log |
18 | } |
19 | } |
20 | |
21 |
4、不应写非常大的try - catch块。
有些甚至将一个方法直接放在一个try-chach块中,这是一种非常不好的方法。在一个try-chach块中应只有一段特定的代码,这样有助于定位到产生异常的位置。
5、异常捕获的顺序
特殊的异常放在前面的catch子句、即如果Exception之间有继承关系,把子类放在前面的catch子句中,把父类放到后面的catch子句中(如代码A)。如果时代码B则catch (XmlException ex)将永远不会执行。
A:
1 | try |
2 | { |
3 | using (XmlReader topicReader = XmlReader.Create (xmlStream, readersetting)) |
4 | { |
5 | // xml processing |
6 | } |
7 | } |
8 | catch (XmlException ex) |
9 | { |
10 | // handler xml exception |
11 | } |
12 | catch (Exception ex) |
13 | { |
14 | // handler xml exception |
15 | } |
16 |
B:
1 | try |
2 | { |
3 | using (XmlReader topicReader = XmlReader.Create (xmlStream, readersetting)) |
4 | { |
5 | // xml processing |
6 | } |
7 | } |
8 | catch (Exception ex) |
9 | |
10 | { |
11 | // handler exception |
12 | } |
13 | catch (XmlException ex) |
14 | |
15 | { |
16 | // handler xml exception |
17 | } |
18 | |
19 |
6、重抛异常
代码A,使用throw ex; 将会打破异常的代码跟踪栈、如果仅仅是重新抛出底层的异常,则使用代码B.如果想抛出自定义封装后的异常,且不打破代码跟踪栈则使用代码C.(将底层异常ex作为 ,自定义封装异常的 inter exception 参数 抛出).
A:
1 | try |
2 | { |
3 | // Do something |
4 | } |
5 | |
6 | catch(Exception ex) |
7 | { |
8 | throw ex; |
9 | } |
10 |
B:
1 | try |
2 | { |
3 | // Do something |
4 | } |
5 | catch |
6 | { |
7 | throw; |
8 | |
9 | } |
C:
1 | try |
2 | { |
3 | // Do something |
4 | } |
5 | catch(Exception ex) |
6 | { |
7 | throw new Exception("封装后的异常",ex); |
8 | } |
9 |