作者 | 羽生结弦责编 | 胡雪蕊出品 | CSDN(ID:CSDNnews)
Try 以及异常在C#中是很重要的内容,很多开发人员似乎并不是很了解Try 和异常。在这篇文章中我将会各你们具体讲解一下Try和异常。
try、catch、finally基本介绍
1.try
try 语句是拿来进行错误处理或则清除错误的代码块。
2.catch
catch 代码块可以直接访问Exception对象,这个对象中包含了相关的错误信息,catch块一般拿来处理错误,或者重新排除异常。
3.finally
finally 代码块降低了程序的确定性,CLR会竭力去执行它。finally 通常会被拿来做清除任务。
注意:finally 代码块并不是一定执行的,在个别情况下finally块也会不执行。try句子前面必须紧随 catch 代码块或则 finally 代码块(也可以二者都存在)。当 try 中的代码发生错误时,如果存在catch代码块,那么它将会被将会被执行,如果只存在 finally 代码块的话,他将在 try 代码块执行完毕后执行,如果存在 catch 代码块和 finally 代码块的话,finally 代码块将在 catch 代码块执行完毕后执行。finally 主要的作用是不管 try 中是否发生错误,都要执行清除代码。现在我们通过一个事例来看一下:
csharp
class Program
{
class Calculation
{
public int Division(int num)
{
try
{
return 10 / num;
}
catch (DivideByZeroException ex)
{
Console.WriteLine("0 不能作为除数");
return -999;
}
}
}
static void Main(string[] args)
{
Calculation calculation = new Calculation();
calculation.Division(0);
Console.Read();
}
}
这段代码中 try 代码块会报 DivideByZeroException错误,因为我们晓得 除数不可能为0。但是这儿不会影响程序的运行,因为报错的代码坐落 try 中,try 将这个错误捕获到后,转给了 catch ,catch 对这个错误进行了处理。
try 语句块旁边的 catch 语句块可以有零个,也可以有 1个,也可以有多个。如果有多个 catch 语句块的话,应该遵守从小到大的次序编撰,所谓的从小到大就是,先捕获可以预见到异常比如前面事例中的DivideByZeroException异常,再捕获其他不可预见到的异常。我们把上面的代码改动一下,来看一下:
csharp
class Program
{
class Calculation
{
public int Division(int num)
{
try
{
return 10 / num;
}
catch (DivideByZeroException ex)
{
Console.WriteLine("0 不能作为除数");
return -999;
}
catch (Exception ex)
{
Console.WriteLine("其他异常");
return -999;
}
}
}
static void Main(string[] args)
{
Calculation calculation = new Calculation();
calculation.Division(0);
Console.Read();
}
}
我们在前面的代码中降低了一个catch (Exception ex) ,这个异常是所有异常的父类,它可以捕获所有任意类型的异常,因此须要把它放到所有 catch 语句块的旁边,如果将它置于所有 catch 语句块的后面,将会难以通过编译。4.try、catch、finally 执行原理当抛出异常时,CLR会进行一个测试,判断当前是否在执行 try 中,并且能被 catch 捕获。如果是的话,抛出的错误将会传递个能兼容这个异常的 catch 代码块中,当 catch 处理完毕后将执行 try、catch 后面的句子,如果存在 finally 代码块,那么将会先执行 finally 代码块,再执行前面的句子。如果不是,CLR 会将这个错误向下抛出给 函数的调用者,并重复这个过程。注意:这里所说的能兼容这个异常的 catch 代码块指的是与这个异常的类型相等的类型,或者是Exception 。
Catch 详解
catch 代码块指定要补货的异常类型,这个异常类型必须是Exception或则它的泛型。我在上面的小节也说过,Exception捕获的是任何类型的错误,那么一定会导致在代码中滥用 Exception ,这里我就说一下在哪些情况下须要使用到 Exception:
1. 无论哪些类型的异常,程序都可能从异常中恢复;
2. 需要重新抛出异常,比如不在当前代码中处理,而是下层代码中处理,或者须要记录错误日志;
3. 阻止出现异常时程序被中止。
除了上述情况外,我们必须针对特定类型的异常,执行特定的 catch 处理异常,例如上面小节中,处理除数为0的DivideByZeroExceptioncatch 代码块。如果代码存在多种异常的话,可以使用多个 catch 进行处理不同的异常,并且针对给定的异常,只有一个 catch 会执行。在须要多个 catch 的情况下,我建议将Exception这个 catch 作为最后一个异常,这样当异常不是已定义的某个具体异常时,最后这个异常可以捕获,防止程序被中止。我们再来看一下反例:
csharp
static void Main(string[] args)
{
try
{
File.Delete(@"d:\123.txt");
Directory.Delete(@"D:\hahaha");
}
catch (DirectoryNotFoundException ex)
{
Console.WriteLine("目录未找到");
}
catch (FileNotFoundException ex)
{
Console.WriteLine("文件不存在");
}
catch(Exception ex)
{
Console.WriteLine("其他异常!");
}
Console.ReadLine();
}
在这个反例中,一共有三个 catch 语句块,第一个是处理目录不存在异常的,第二个是处理文件不存在异常的,最后一个是拿来处理其他异常的。
catch 特殊用法
1. 省略异常变量
有时候我们并不需要晓得异常的详情,这个时侯我们就可以省略掉异常变量,代码如下:
csharp
catch (DirectoryNotFoundException)
{
Console.WriteLine("目录未找到");
}
2. 省略异常类型与省略异常变量一样,有时候我们也不需要异常类型,这时我们就可以省略掉异常类型。当我们省略掉异常类型时,catch 块将会捕获所有类型的异常。代码如下:
csharp
catch
{
Console.WriteLine("所有异常类型");
}
3. 过滤异常
有些异常有可能是多种缘由造成的,比如WebException异常,有可能是恳求超时、请求地址不存在等问题导致的,但是我们只想处理超时引起的错误,这时我们只需在 catch 后面加上when关键字进行过滤即可,当符合过滤条件的话会执行 catch 中的处理句子,如果不符合将会执行前面符合异常条件的 catch 语句块,代码如下:
csharp
catch (WebException ex) when (ex.Status == WebExceptionStatus.Timeout)
{
Console.WriteLine("超时");
}
finally
finally 代码块在大部分情况下就会被执行的,不管try 中的代码是否执行完毕,是否有异常抛出。当如下三种情况时 finally 将会被执行:
1. 执行完一个 catch 代码块后;
2. return 语句跳出 try 代码块或则执行离开 try 代码块;
3. try 代码块执行完毕。
我昨天也说过,finally 在大部分情况下就会被执行,那么在哪些情况下不会被执行呢?只有程序被强行中止或则在 try 代码块或 catch 代码块中存在无线死循环的情况下,finally 才不会被执行。一般情况下我们借助 finally 进行清除代码。我们看一下 finally 的事例:
csharp
static void Main(string[] args)
{
StreamReader streamReader = File.OpenText(@"d:\123.txt");
try
{
if (streamReader.EndOfStream)
{
return;
}
Console.WriteLine(streamReader.ReadToEnd());
}
finally
{
if (streamReader != null)
{
streamReader.Dispose();
}
}
Console.ReadLine();
}
上面代码中我们在 try中读取文件内容并输出,在 finally 将所占用的资源释放掉。
特殊的finally
有一种特殊的 finally ,它就是我们常常看到的using,using 可以拿来释放类中的非托管资源,比如数据库联接、文件处理等。这些类都实现了IDisposable插口,通过这个插口中的Dispose方式可以释放非托管资源。例如我们将上面读取文件内容的代码更改如下,同样可以实现里面 finally 中的疗效:
csharp
static void Main(string[] args)
{
using (StreamReader streamReader = File.OpenText(@"d:\123.txt"))
{
if (streamReader.EndOfStream)
{
return;
}
Console.WriteLine(streamReader.ReadToEnd());
Console.ReadLine();
}
}
异常抛出
1.手动抛出异常
异常除了可以被运行时抛出,用户还可以自动抛出异常,例如我们自动抛出一个文件不存在异常,代码如下:
csharp
static void Main(string[] args)
{
//..more code
throw new FileNotFoundException();
//..more code
}
在三元运算符中,也可以出现异常,代码如下:
csharp
public int SpecificSize(int a, int b)
{
return a > b ? throw new Exception("a>b") :a+b;
}
2. 重新抛出异常加入我们不需要在当前方式中处理异常,我们就须要重新抛出异常,只须要在 catch 代码块中使用throw即可,代码如下:
csharp
catch(DirectoryNotFoundException e)
{
throw e;
}
注意:当我们使用 throw e 抛出异常的话,调用方接收到异常的stackfrace属性将会发生改变,不会反映出原始异常,如果须要调用发接收原始异常的话,只须要throw即可。
异常抛出的特殊情况
1.抛出更具体的异常有时候我们须要抛出更具体的异常,例如下边的反例:
csharp
catch(Exception e)
{
throw new DivideByZeroException("除零异常",e);
}
上面代码中的这些情况就是抛出更具体的异常,这里须要注意的有两点:
(1)更具体的异常要比 catch 的异常类型范围要小;
(2)将异常变量作为参数传递给更具体的异常。
2. 抛出具象异常
抛出具象异常的目的,是因为须要穿越信任边界,防止信息泄漏。
下面我将列举常用的异常属性:
异常属性描述:
下面是常用的异常类型:
异常类型描述:
作者简介:朱钢,笔名羽生结弦,CSDN博客专家,.NET中级开发工程师,7年一线开发经验,参与过电子政务系统和AI客服系统的开发,以及互联网急聘网站的构架设计,目前就职于北京恒创融慧科技发展有限公司,从事企业级安全监控系统的开发。
【END】
技术人在关注TA!戳↓↓↓
热 文推 荐
重
亚
点击阅读原文,输入关键词,即可搜索您想要的 CSDN 文章。
你点的每个“在看”,我都认真当成了喜欢