这样的代码有什么区别:
string path = @"c:userspublictest.txt";
System.IO.StreamReader file = new System.IO.StreamReader(path);
char[] buffer = new char[10];
try
{
file.ReadBlock(buffer, index, buffer.Length);
}
catch (System.IO.IOException e)
{
Console.WriteLine("Error reading from {0}. Message = {1}", path, e.Message);
}
finally
{
if (file != null)
{
file.Close();
}
}
而这个:
string path = @"c:userspublictest.txt";
System.IO.StreamReader file = new System.IO.StreamReader(path);
char[] buffer = new char[10];
try
{
file.ReadBlock(buffer, index, buffer.Length);
}
catch (System.IO.IOException e)
{
Console.WriteLine("Error reading from {0}. Message = {1}", path, e.Message);
}
if (file != null)
{
file.Close();
}
在这个结构中真的是最终需要的块。为什么Microsoft提供这样的结构?这似乎是多余的。不是吗?
想象一下,如果发生了一些你没有处理过的其他异常,例如ArgumentOutOfRangeException
,或者如果你想重新抛出异常或从你的catch块中抛出一个包装的异常:
-
第一个块将确保关闭文件,无论是否发生异常。
-
第二个块仅在未发生异常或发生
IOException
时关闭文件。它不处理任何其他情况。
:注:第一个块还确保在从 catch 块本身内部抛出异常时关闭文件。
即使存在未捕获的异常,第一个块也会关闭文件。
仅当没有异常或捕获任何引发的异常时,第二个块才会关闭文件。
第一个也将确保如果try
具有break
、goto
、return
、continue
或任何其他会导致执行移动到try
块之外的跳转结构,则文件被关闭。 第二个不会,因此可能会导致资源未关闭。
在您的示例中,如果代码引发 System.IO.IOException
以外的异常,则不保证清理代码会运行。使用 finally
块,无论引发哪种类型的异常,其中的代码都将运行。
想象一下catch{}
内部有一个异常,里面的代码最终仍然可以运行,但if (file != null){}
块不会。
它是多余的。
例如,如果您将重新抛出异常并且仍然希望在块之后运行一些代码,则非常有用:
try {
// do something dangerous
} catch(...) {
// log the error or something
throw; // let the exception bubble up to the caller
} finally {
// this always runs
}
// this only runs if there was no exception
另一个示例是,如果 catch 可能出于其他原因引发异常:
try {
// do something dangerous
} catch(...) {
// handle the error
// log the error, which may cause a different exception
} finally {
// this runs even if the catch crashed
}
// this only runs if there was no exception, or the code in the catch worked
简单地说,由于代码可能会由于您可能甚至不知道的很多原因而崩溃,因此将清理放在finally
块中以确保它无论发生什么都会运行是很有用的。