C# 异常处理最佳实践,解决代码分析提示CA1031:不要捕捉一般异常类型的解决办法
异常类型
异常一般分为系统异常 和 应用异常。系统异常有无法连接数据库,而应用异常是业务逻辑异常,比如授权失败。
在 C# 中异常基于 System.Exception
,派生出 System.SystemException
和 System.ApplicationException
。微软最初设计为 CLR 抛出的异常都继承自 System.SystemException
,应用程序抛出的异常应当继承自 System.ApplicationException
。但 .NET 框架类库(FCL) 没能很好地遵循这个原则。因此,目前存在的 System.SystemException
和 System.ApplicationException
主要用于兼容。
- 在实际业务中,不应该使用
System.SystemException
和System.ApplicationException
- 不要在非顶层中捕获
System.Exception
,除非你会在捕获后重新抛出 - 在对象不正确的时候,可使用
System.InvalidOperationException
和System.ArgumentException
和其派生异常。例如给只读对象赋值 - 业务异常应该定义一个基于
System.Exception
的自定义异常,并基于自定义的异常再派生具体的异常。例如业务异常为BusinessException:System.Exception
,转账业务异常为TransferFundsException:BusinessException
- 需要给异常填写异常描述
- 不要自己抛出
System.StackOverflowException
这类异常 - 并不是所有情况都需要使用
try catch
语句,可使用if
提前判断是否抛出异常,以提高性能
使用 throw
和 throw ex
的区别
在 C#
中推荐使用 throw
,原因是如果直接使用 throw ex
会清除原始的异常,将 ex
作为新的异常源,从而找不到真正的异常源。
建议只在最外层做捕获。
// 错误的用法 try { } catch(Exception ex) { throw ex; } // 正确的用法 try { } catch { throw; } // 正确的用法 try { } catch(Exception ex) { throw new Exception("message", ex); } // 正确的用法 throw new AppException("message");
CA1031:不要捕捉一般异常类型
原因
一般异常(如 System.Exception 或 System.SystemException)在 catch
语句中捕获,或者使用 catch()
等常规 catch 子句。
规则说明
不应捕捉一般异常。
如何解决冲突
若要修复与此规则的冲突,请捕获更具体的异常,或者再次引发一般异常作为 catch
块中的最后一条语句。
何时禁止显示警告
不禁止显示此规则发出的警告。 捕获一般异常类型可以隐藏库用户的运行时问题,并且可能会使调试变得更加困难。
示例
下面的示例显示一个与此规则冲突的类型和一个正确实现 catch
块的类型。
using System; using System.IO; namespace DesignLibrary { // Creates two violations of the rule. public class GenericExceptionsCaught { FileStream inStream; FileStream outStream; public GenericExceptionsCaught(string inFile, string outFile) { try { inStream = File.Open(inFile, FileMode.Open); } catch(SystemException e) { Console.WriteLine("Unable to open {0}.", inFile); } try { outStream = File.Open(outFile, FileMode.Open); } catch { Console.WriteLine("Unable to open {0}.", outFile); } } } public class GenericExceptionsCaughtFixed { FileStream inStream; FileStream outStream; public GenericExceptionsCaughtFixed(string inFile, string outFile) { try { inStream = File.Open(inFile, FileMode.Open); } // Fix the first violation by catching a specific exception. catch(FileNotFoundException e) { Console.WriteLine("Unable to open {0}.", inFile); } try { outStream = File.Open(outFile, FileMode.Open); } // Fix the second violation by re-throwing the generic // exception at the end of the catch block. catch { Console.WriteLine("Unable to open {0}.", outFile); throw; //手动高亮 } } } }