我知道如何使用 try-catch-finally。但是我没有得到使用的进步,finally
因为我总是可以将代码放在 try-catch 块之后。有什么明确的例子吗?
11 回答
你需要一个 finally 因为你不应该总是有一个问题:
void M()
{
var fs = new FileStream(...);
try
{
fs.Write(...);
}
finally
{
fs.Close();
}
}
上述方法不会从 using 中捕获错误,而是fs
将它们留给调用者。但它应该始终关闭流。
请注意,这种代码通常会使用 using() {}
块,但这只是 try/finally 的简写。要完整:
using(var fs = new FileStream(...))
{
fs.Write(...);
} // invisible finally here
它几乎总是用于清理,通常通过using
语句隐含:
FileStream stream = new FileStream(...);
try
{
// Read some stuff
}
finally
{
stream.Dispose();
}
现在这不等于
FileStream stream = new FileStream(...);
// Read some stuff
stream.Dispose();
因为“读取一些东西”代码可能会抛出异常或可能返回 - 无论它完成,我们想要处理流。
所以finally
块通常用于某种资源清理。但是,在 C# 中,它们通常通过using
语句隐含:
using (FileStream stream = new FileStream(...))
{
// Read some stuff
} // Dispose called automatically
finally
块在 Java 中比在 C# 中更常见,正是因为该using
语句。我很少用finally
C# 编写自己的块。
try
{
DoSomethingImportant();
}
finally
{
ItIsRidiculouslyImportantThatThisRuns();
}
当你有一个 finally 块时,其中的代码保证在 try 退出时运行。如果您将代码放在 try/catch 之外,情况并非如此。一个更常见的示例是在您使用using
语句时与一次性资源一起使用的示例。
using (StreamReader reader = new StreamReader(filename))
{
}
扩展到
StreamReader reader = null;
try
{
reader = new StreamReader(filename);
// do work
}
finally
{
if (reader != null)
((IDisposable)reader).Dispose();
}
这确保了所有非托管资源都得到处置和释放,即使在try
.
*请注意,在某些情况下,控件不会退出 try,并且 finally 不会实际运行。作为一个简单的例子,PowerFailureException
.
finally
即使在以下情况下,也会执行放入块中的代码:
- or块中有
return
语句ORtry
catch
- 该
catch
块重新抛出异常
例子:
public int Foo()
{
try
{
MethodThatCausesException();
}
catch
{
return 0;
}
// this will NOT be executed
ReleaseResources();
}
public int Bar()
{
try
{
MethodThatCausesException();
}
catch
{
return 0;
}
finally
{
// this will be executed
ReleaseResources();
}
}
更新:这实际上不是一个很好的答案。另一方面,也许这是一个很好的答案,因为它说明了一个完美的finally
成功示例,开发人员(即我)可能无法确保正确清理。在下面的代码中,考虑抛出异常以外的情况SpecificException
。那么第一个示例仍然会执行清理,而第二个则不会,尽管开发人员可能会认为“我捕获了异常并处理了它,所以后面的代码肯定会运行”。
每个人都在给出使用try
/finally
没有catch
. 即使您抛出异常,使用a这样做仍然有意义。catch
考虑要返回值的情况*。
try
{
DoSomethingTricky();
return true;
}
catch (SpecificException ex)
{
LogException(ex);
return false;
}
finally
{
DoImportantCleanup();
}
上面没有a的替代方案finally
(在我看来)可读性较差:
bool success;
try
{
DoSomethingTricky();
success = true;
}
catch (SpecificException ex)
{
LogException(ex);
success = false;
}
DoImportantCleanup();
return success;
*我确实认为/ /的一个更好的例子是在块中重新抛出异常(使用,而不是- 但这是另一个主题),因此这是必要的,因为/之后的代码不会运行。这通常是通过对资源的声明来完成的,但情况并非总是如此。有时清理不是专门的调用(或不仅仅是调用)。try
catch
finally
throw
throw ex
catch
finally
try
catch
using
IDisposable
Dispose
Dispose
您不一定要在例外情况下使用它。您可能必须在每个块try/finally
之前执行一些清理。return
无论是否获得错误,都会始终执行 finally 块。它通常用于清理目的。
对于您的问题,Catch 的一般用途是将错误返回给调用者,在这种情况下,代码最终仍会执行。
即使在 catch 块中重新抛出异常,finally 块也将始终执行。
我不确定它是如何在 c# 中完成的,但在 Delphi 中,您会经常发现“终于”。关键字是手动内存管理。
MyObject := TMyObject.Create(); //Constructor
try
//do something
finally
MyObject.Free();
end;
如果在 catch 块中发生(或重新抛出)异常,则不会执行 catch 之后的代码 - 相反,finally 中的代码仍将被执行。
此外,finally 中的代码甚至在使用 return 退出方法时也会执行。
finally 在处理需要关闭的文件等外部资源时特别方便:
Stream file;
try
{
file = File.Open(/**/);
//...
if (someCondition)
return;
//...
}
catch (Exception ex)
{
//Notify the user
}
finally
{
if (file != null)
file.Close();
}
但是请注意,在此示例中,您还可以使用using:
using (Stream file = File.Open(/**/))
{
//Code
}
例如,在此过程中,您可以禁用 WinForm...
try
{
this.Enabled = false;
// some process
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
this.Enabled = true;
}