10

我知道如何使用 try-catch-finally。但是我没有得到使用的进步,finally因为我总是可以将代码放在 try-catch 块之后。有什么明确的例子吗?

4

11 回答 11

4

你需要一个 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
于 2011-02-11T13:19:02.490 回答
4

它几乎总是用于清理,通常通过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语句。我很少用finallyC# 编写自己的块。

于 2011-02-11T13:19:28.333 回答
3
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.

于 2011-02-11T13:18:39.483 回答
2

finally即使在以下情况下,也会执行放入块中的代码:

  • or块中有return语句ORtrycatch
  • 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();
  }
}
于 2011-02-11T13:21:38.787 回答
2

更新:这实际上不是一个很好的答案。另一方面,也许这一个很好的答案,因为它说明了一个完美的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;

*我确实认为/ /的一个更好的例子是在块中重新抛出异常(使用,而不是- 但这是另一个主题),因此这是必要的,因为/之后的代码不会运行。这通常是通过对资源的声明来完成的,但情况并非总是如此。有时清理不是专门的调用(或不仅仅是调用)。trycatchfinallythrow throw excatchfinallytrycatchusingIDisposableDisposeDispose

于 2011-02-11T13:27:07.173 回答
1

您不一定要在例外情况下使用它。您可能必须在每个块try/finally之前执行一些清理。return

于 2011-02-11T13:18:53.673 回答
1

无论是否获得错误,都会始终执行 finally 块。它通常用于清理目的。

对于您的问题,Catch 的一般用途是将错误返回给调用者,在这种情况下,代码最终仍会执行。

于 2011-02-11T13:19:52.493 回答
0

即使在 catch 块中重新抛出异常,finally 块也将始终执行。

于 2011-02-11T13:19:23.090 回答
0

我不确定它是如何在 c# 中完成的,但在 Delphi 中,您会经常发现“终于”。关键字是手动内存管理。

MyObject := TMyObject.Create(); //Constructor
try 
     //do something
finally
    MyObject.Free(); 
end;
于 2011-02-11T13:20:01.973 回答
0

如果在 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
}
于 2011-02-11T13:22:07.807 回答
0

例如,在此过程中,您可以禁用 WinForm...

try
{
this.Enabled = false;
// some process
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
finally
{
this.Enabled = true;
}
于 2011-02-11T13:23:50.733 回答