杂谈try-catch-finally异常处理

 更新时间:2020年6月25日 11:25  点击:1728

相关阅读:再谈异常处理try catch finally

1. 前言

最近这段时间正开发一个店铺管理系统,这个项目定位于给中小型店铺使用的软件系统。简单的说,它处理商品的进货,销售,退货等功能。软件虽小,五脏俱全,里面涉及的技术跟大型应用软件其实差别也不大,其中有加密、数据访问、异常处理、日志、验证、ORM、依赖注入等。

本篇文章主要介绍C#语言的异常处理方面的内容,其中包含的主要内容:

•什么是异常?异常的特点?
•异常处理的基础知识。
•引发和捕捉异常的处理准则。
•避免与异常相关的性能问题的两种设计模式。
•微软企业库异常处理模块。

2. 异常概述

•在应用程序遇到异常情况(如被零除情况或内存不足警告)时,就会产生异常。
•在可能引发异常的语句周围使用 try 块。
•try 块中发生异常后,控制流会立即跳转到关联的异常处理程序(如果存在)。
•如果给定异常没有异常处理程序,则程序将停止执行,并显示一条错误消息。
•如果 catch 块定义了一个异常变量,则可以使用它来获取有关所发生异常的类型的更多信息。
•可能导致异常的操作通过 try 关键字来执行。
•异常处理程序是在异常发生时执行的代码块。在 C# 中,catch 关键字用于定义异常处理程序。
•程序可以使用 throw 关键字显式地引发异常。
•异常对象包含有关错误的详细信息,比如调用堆栈的状态以及有关错误的文本说明。
•即使引发了异常,finally 块中的代码也会执行,从而使程序可以释放资源。

3. 异常处理基础知识

3.1. 如何:使用 Try/Catch 块捕捉异常

将可能引发异常的代码节放在 Try 块中,而将处理异常的代码放在 Catch 块中。Catch 块是一系列以关键字 catch 开头的语句,语句后跟异常类型和要执行的操作。

下面的代码示例使用 Try/Catch 块捕捉可能的异常。Main 方法包含带有 StreamReader 语句的 Try 块,该语句打开名为 data.txt 的数据文件并从该文件写入字符串。Try 块后面是 Catch 块,该块捕捉 Try 块产生的任何异常。

using System;
using System.IO;
using System.Security.Permissions;
// Security permission request.
[assembly:FileIOPermissionAttribute(SecurityAction.RequestMinimum, All = @"c:\data.txt")]
public class ProcessFile {
public static void Main() {
try {
StreamReader sr = File.OpenText("data.txt");
Console.WriteLine("The first line of this file is {0}", sr.ReadLine()); 
}
catch(Exception e) {
Console.WriteLine("An error occurred: '{0}'", e);
}
}
}

3.2. 如何:在 Catch 块中使用特定异常

发生异常时,异常沿堆栈向上传递,每个 Catch 块都有机会处理它。Catch 语句的顺序很重要。将针对特定异常的 Catch 块放在常规异常 Catch 块的前面,否则编译器可能会发出错误。确定正确 Catch 块的方法是将异常的类型与 Catch 块中指定的异常名称进行匹配。如果没有特定的 Catch 块,则由可能存在的常规 Catch 块捕捉异常。

下面的代码示例使用 try/catch 块捕获 InvalidCastException。该示例创建一个名为 Employee 的类,它带有一个属性:职员级别 (Emlevel)。PromoteEmployee 方法取得对象并增加职员级别。将 DateTime 实例传递给 PromoteEmployee 方法时,发生 InvalidCastException。

using System;
public class Employee
{
//Create employee level property.
public int Emlevel
{
get
{
return(emlevel);
}
set
{
emlevel = value;
}
}
int emlevel;
}
public class Ex13
{
public static void PromoteEmployee(Object emp)
{
//Cast object to Employee.
Employee e = (Employee) emp;
// Increment employee level.
e.Emlevel = e.Emlevel + 1;
}
public static void Main()
{
try
{
Object o = new Employee();
DateTime newyears = new DateTime(2001, 1, 1);
//Promote the new employee.
PromoteEmployee(o);
//Promote DateTime; results in InvalidCastException as newyears is not an employee instance.
PromoteEmployee(newyears);
}
catch (InvalidCastException e)
{
Console.WriteLine("Error passing data to PromoteEmployee method. " + e);
}
}
}

3.3. 如何:显式引发异常

可以使用 throw 语句显式引发异常。还可以使用 throw 语句再次引发捕获的异常。较好的编码做法是,向再次引发的异常添加信息以在调试时提供更多信息。

下面的代码示例使用 try/catch 块捕获可能的 FileNotFoundException。try 块后面是 catch 块,catch 块捕获 FileNotFoundException,如果找不到数据文件,则向控制台写入消息。下一条语句是 throw 语句,该语句引发新的 FileNotFoundException 并向该异常添加文本信息。

using System;
using System.IO;
public class ProcessFile
{
public static void Main()
{
FileStream fs = null;
try 
{
//Opens a text tile.
fs = new FileStream(@"C:\temp\data.txt", FileMode.Open);
StreamReader sr = new StreamReader(fs);
string line;
//A value is read from the file and output to the console.
line = sr.ReadLine();
Console.WriteLine(line);
}
catch(FileNotFoundException e)
{
Console.WriteLine("[Data File Missing] {0}", e);
throw new FileNotFoundException(@"data.txt not in c:\temp directory]",e);
}
finally
{
if (fs != null)
fs.Close();
}
}
}

3.4. 如何:使用 Finally 块

异常发生时,执行将终止,并且控制交给最近的异常处理程序。这通常意味着不执行希望总是调用的代码行。有些资源清理(如关闭文件)必须总是执行,即使有异常发生。为实现这一点,可以使用 Finally 块。Finally 块总是执行,不论是否有异常发生。

下面的代码示例使用 try/catch 块捕获 ArgumentOutOfRangeException。Main 方法创建两个数组并试图将一个数组复制到另一个数组。该操作生成 ArgumentOutOfRangeException,同时错误被写入控制台。Finally 块执行,不论复制操作的结果如何。

using System;
class ArgumentOutOfRangeExample
{
static public void Main()
{
int[] array1={0,0};
int[] array2={0,0};
try
{
Array.Copy(array1,array2,-1);
}
catch (ArgumentOutOfRangeException e)
{
Console.WriteLine("Error: {0}",e);
}
finally
{
Console.WriteLine("This statement is always executed.");
}
}
}

4. 异常设计准则

4.1. 异常引发

•不要返回错误代码。异常是报告框架中的错误的主要手段。
•尽可能不对正常控制流使用异常。除了系统故障及可能导致争用状态的操作之外,框架设计人员还应设计一些 API 以便用户可以编写不引发异常的代码。例如,可以提供一种在调用成员之前检查前提条件的方法,以便用户可以编写不引发异常的代码。
•不要包含可以根据某一选项引发或不引发异常的公共成员。
•不要包含将异常作为返回值或输出参数返回的公共成员。
•考虑使用异常生成器方法。从不同的位置引发同一异常会经常发生。为了避免代码膨胀,请使用帮助器方法创建异常并初始化其属性。
•避免从 finally 块中显式引发异常。可以接受因调用引发异常的方法而隐式引发的异常。

4.2. 异常处理

•不要通过在框架代码中捕捉非特定异常(如 System.Exception、System.SystemException 等)来处理错误。
•避免通过在应用程序代码中捕捉非特定异常(如 System.Exception、System.SystemException 等)来处理错误。某些情况下,可以在应用程序中处理错误,但这种情况极。
•如果捕捉异常是为了传输异常,则不要排除任何特殊异常。
•如果了解特定异常在给定上下文中引发的条件,请考虑捕捉这些异常。
•不要过多使用 catch。通常应允许异常在调用堆栈中往上传播。
•使用 try-finally 并避免将 try-catch 用于清理代码。在书写规范的异常代码中,try-finally 远比 try-catch 更为常用。
•捕捉并再次引发异常时,首选使用空引发。这是保留异常调用堆栈的最佳方式。
•不要使用无参数 catch 块来处理不符合 CLS 的异常(不是从 System.Exception 派生的异常)。支持不是从 Exception 派生的异常的语言可以处理这些不符合 CLS 的异常。

5. 两种设计模式

5.1. Tester-Doer 模式

Doer 部分

public class Doer
{
public static void ProcessMessage(string message)
{
if (message == null)
{
throw new ArgumentNullException("message");
}
}
}

Tester部分

public class Tester
{
public static void TesterDoer(ICollection<string> messages)
{
foreach (string message in messages)
{
if (message != null)
{
Doer.ProcessMessage(message);
}
}
}
}

5.2. TryParse 模式

TryParse 方法类似于 Parse 方法,不同之处在于 TryParse 方法在转换失败时不引发异常。

Parse方法

public void Do()
{
string s = “a”;
double d;
try
{
d = Double.Parse(s);
}
catch (Exception ex)
{
d = 0;
}
}

TryParse方法

public void TryDo()
{
string s = "a";
double d;
if (double.TryParse(s, out d) == false)
{
d = 0;
}
}

6. 微软企业库异常处理模块

6.1. 创建自定义异常包装类

public class BusinessLayerException : ApplicationException
{
public BusinessLayerException() : base()
{
}
public BusinessLayerException(string message) : base(message)
{
}
public BusinessLayerException(string message, Exception exception) :
base(message, exception)
{
}
protected BusinessLayerException(SerializationInfo info, StreamingContext context) :
base(info, context)
{
}
}

6.2. 配置异常处理

<add name="Wrap Policy">
<exceptionTypes>
<add type="System.Data.DBConcurrencyException, System.Data, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
postHandlingAction="ThrowNewException" name="DBConcurrencyException">
<exceptionHandlers>
<add exceptionMessage="Wrapped Exception: A recoverable error occurred while attempting to access the database."
exceptionMessageResourceType="" wrapExceptionType="ExceptionHandlingQuickStart.BusinessLayer.BusinessLayerException, ExceptionHandlingQuickStart.BusinessLayer"
type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WrapHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=3.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"
name="Wrap Handler" />
</exceptionHandlers>
</add>
</exceptionTypes>
</add>

6.3. 编写代码

public bool ProcessWithWrap()
{
try
{
this.ProcessB();
}
catch(Exception ex)
{
// Quick Start is configured so that the Wrap Policy will
// log the exception and then recommend a rethrow.
bool rethrow = ExceptionPolicy.HandleException(ex, "Wrap Policy");
if (rethrow)
{
throw; 
} 
}
return true;
}

小结

try { //执行的代码,其中可能有异常。一旦发现异常,则立即跳到catch执行。否则不会执行catch里面的内容 }
catch { //除非try里面执行代码发生了异常,否则这里的代码不会执行 }
finally { //不管什么情况都会执行,包括try catch 里面用了return ,可以理解为只要执行了try或者catch,就一定会执行 finally }

[!--infotagslink--]

相关文章

  • C++异常处理入门(try和catch)

    C++ 提供了异常机制,让我们能够捕获运行时错误,本文就详细的介绍了C++异常处理入门,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-08-09
  • C#中try...catch的使用与常见面试题分享

    这篇文章首先给大家介绍了关于C#中try...catch的语法,而后又给大家分享了关于C#中try...catch最常见的面试题,具有一定的参考借鉴价值,需要的朋友们下面来一起看看吧。...2020-06-25
  • 如何使用Sentry 监控你的Spring Boot应用

    这篇文章主要介绍了如何使用Sentry 监控你的Spring Boot应用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-09
  • Java catch与throw同时使用的操作

    这篇文章主要介绍了Java catch与throw同时使用的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-25
  • 学好PHP,try your best

    最近,自己利用放假时间学PHP,可能大家都不知道PHP是什么呢?PHP是一种网页嵌入式语言,就同ASP,JSP一样,这应该是编程中最容易学习的语言。前几年自己在华师上夜大...2016-11-25
  • php中try catch捕获异常实例详解

    这篇文章主要介绍了php中try catch捕获异常实例详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-08-07
  • java:try...catch跳过异常继续处理循环问题

    这篇文章主要介绍了java:try...catch跳过异常继续处理循环问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-10-02
  • 浅谈PHP中try{}catch{}的使用方法

    下面小编就为大家带来一篇浅谈PHP中try{}catch{}的使用方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2017-01-08
  • SpringBoot整合spring-retry实现接口请求重试机制及注意事项

    今天通过本文给大家介绍我们应该如何使用SpringBoot来整合spring-retry组件实现重试机制及注意事项,本文通过实例代码给大家介绍的非常详细,需要的朋友参考下吧...2021-08-14
  • java异常:异常处理--try-catch结构详解

    今天小编就为大家分享一篇关于Java异常处理之try...catch...finally详解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧...2021-09-08
  • JavaScript语句错误throw、try及catch实例解析

    这篇文章主要介绍了JavaScript语句错误throw、try及catch实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下...2020-08-19
  • .net 一些无法catch的异常

    .net 一些无法catch的异常,需要的朋友可以参考一下...2020-06-25
  • PHP Try-catch 语句使用技巧

    这篇文章主要介绍了PHP Try-catch 语句使用技巧,需要的朋友可以参考下...2016-03-02
  • C++ 异常处理 catch(...)介绍

    catch(…)能够捕获多种数据类型的异常对象,所以它提供给程序员一种对异常 对象更好的控制手段,使开发的软件系统有很好的可靠性...2020-04-25
  • 浅谈Java中Map和Set之间的关系(及Map.Entry)

    这篇文章主要介绍了浅谈Java中Map和Set之间的关系(及Map.Entry),具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-09-24
  • Android捕捉错误try catch 的简单使用教程

    这篇文章主要介绍了Android捕捉错误try catch 的简单使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2020-09-19
  • C++中try throw catch异常处理的用法示例

    这篇文章主要给大家介绍了关于C++中try throw catch异常处理的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者使用C++具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧...2020-04-25
  • java 异常被catch后 将会继续执行的操作

    这篇文章主要介绍了java 异常被catch后 将会继续执行的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-02-24
  • 杂谈try-catch-finally异常处理

    这篇文章主要介绍了杂谈try-catch-finally异常处理的相关资料,需要的朋友可以参考下...2020-06-25
  • Java try()语句实现try-with-resources异常管理机制操作

    这篇文章主要介绍了Java try()语句实现try-with-resources异常管理机制操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教...2021-09-19