声明
本文内容来自微软 MVP solenovex 的视频教程——真会C#? - 第4章 进阶C#其它内容,大致和第 1 课—— 4.5 Try 和 异常(上) 对应。可在 GitHub 中查看 C# 视频教程的配套PPT
本文主要包括以下内容:
- catch 子句
- finally 块
Try 语句和异常
Try 语句,try 语句指定了用来进行错误处理或清理的一个代码块。try 语句块后边必须紧接着一个 catch 块或者是一个 finally 块,或者两者都有。当 try 块里发生错误的时候,catch 块就会被执行。finally 块会在执行完 try 块之后执行,如果 catch 也执行了,那就在 catch 块后边执行。finally 块用来执行一些清理代码,无论是否有错误发生。
catch 块可以访问一个 Exception 对象,这个 Exception 对象里含有关于错误的信息。catch 块通常被用来对错误进行处理/补偿或者重新抛出异常。
finally 块为你的程序增加了确定性:CLR 总是尽力去执行它。它通常用来做一些清理任务。
try
{
... // exception may get thrown within execution of this block
}
catch (ExceptionA ex)
{
... // handle exception of type ExceptionA
}
catch (ExceptionB ex)
{
... // handle exception of type ExceptionB
}
finally
{
... // cleanup code
}
当异常被抛出的时候,CLR 会执行一个测试,当前是否执行在能够 catch 异常的 try 语句里,如果是:当前执行就会传递给兼容的 catch 块里面,如果 catch 块完成了执行,那么执行会移动到 try 语句后边的语句。如果有 finally 块存在,会先执行 finally 块;如果不是:执行会返回到函数的调用者,并重复这个测试过程(在执行完任何包裹这语句的 finally 块之后)。
catch 子句
catch 子句指定要捕获的异常的类型。这个异常必须是 System.Exception 或其子类。捕获System.Exception 这个异常的话就会捕获所有可能的错误。当处理下面几种情况时,这么做是很有用的:
- 无论是哪种类型的异常,你的程序都可能从错误中恢复
- 你计划重新抛出异常(可能在你记录了 log 之后)
- 你的错误处理器是程序终止运行前的最后一招
更典型的情况是,你会 catch 特定类型的异常。为的是避免处理那些你的处理程序并未针对设计的情况。你可以使用多个 catch 子句来处理多个异常类型。
class Test
{
static void Main (string[] args)
{
try
{
byte b = byte.Parse (args[0]);
Console.WriteLine (b);
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine ("Please provide at least one argument");
}
catch (FormatException ex)
{
Console.WriteLine ("That's not a number!");
}
catch (OverflowException ex)
{
Console.WriteLine ("You've given me more than a byte!");
}
}
}
上例中,针对给定的异常,只有一个 catch 子句会执行。如果你希望有一个兜底的 catch 可以捕获任何类型的异常,那么你需要把特定类型的异常捕获放在靠前的位置。如果你不需要访问异常的属性,那么你可以不指定异常变量。
catch (OverflowException) // no variable
{
...
}
更甚者,你可以把异常类型和变量都拿掉,这也意味着它会捕获所有的异常:catch { ... }
。
异常的过滤
从 C#6 开始,你可以在 catch 子句中添加一个 when 子句来指定一个异常过滤器:
catch (WebException ex) when (ex.Status == WebExceptionStatus.Timeout)
{
...
}
此例中,如果WebException被抛出的话,那么when后边的bool表达式就会被执行估算。如果计算的结果是false,那么后边所有的catch子句都会在考虑范围内。
catch (WebException ex) when (ex.Status == WebExceptionStatus.Timeout)
{ ... }
catch (WebException ex) when (ex.Status == WebExceptionStatus.SendFailure)
{ ... }
finally 块
finally 块永远都会被执行,无论是否抛出异常,无论try块是否跑完,finally 块通常用来写清理代码。finally 块会在以下情况被执行:
- 在一个catch块执行完之后,因为跳转语句(例如 return 或 goto),程序的执行离开了 try 块
- try 块执行完毕后
唯一可以不让 finally 块执行的东西就是无限循环,或者程序突然结束。
static void ReadFile()
{
StreamReader reader = null; // In System.IO namespace
try
{
reader = File.OpenText ("file.txt");
if (reader.EndOfStream) return;
Console.WriteLine (reader.ReadToEnd());
}
finally
{
if (reader != null) reader.Dispose();
}
}
using 语句
很多类都封装了非托管的资源,例如文件处理、图像处理、数据库连接等。这些类都实现了 IDisposable 接口,这个接口定义了一个无参的 Dispose 方法用来清理这些资源。using 语句提供了一个优雅的语法来在 finally 块里调用实现了 IDisposable 接口对象上的 Dispose 方法。
using (StreamReader reader = File.OpenText ("file.txt"))
{
...
}
// is precisely equivalent to:
{
StreamReader reader = File.OpenText ("file.txt");
try
{
...
}
finally
{
if (reader != null)
((IDisposable)reader).Dispose();
}
}

参考
Exceptions and Exception Handling (C# Programming Guide)
try-catch (C# Reference)
using statement (C# Reference)
Exception Class
网友评论