使用 try/finally 是内存管理的好习惯吗?

Is using try/finally a good practice for memory management?

我的一位前辈告诉我对所有方法使用 try/ finally 块来清除初始化对象数据。 例如:

var serviceProxy = new NotificationServiceProxy();
try
{
     return serviceProxy.GetNotifications(userID, request.Filters, request.FilterProperty, usertypeid);
}
finally
{
     serviceProxy = null;
}

这是一个好习惯吗?如果我对我的所有方法都使用 try/ catch 来清除初始化对象数据。

是的,如果你需要处理一个变量。

您不需要清除局部变量。在退出该方法之前,无论如何都会恢复堆栈(这将释放变量使用的堆栈上的 space)并且将局部变量设置为 null 不会释放 space 在堆上(垃圾收集器会这样做)。如果您需要进行一些清理,例如对象处理或关闭文件,请使用 try finally

不是在 C# 中,而是使用 using

using(var serviceProxy = new NotificationServiceProxy()){
    serviceProxy.GetNotifications(userID, request.Filters, request.FilterProperty, usertypeid);
    // Do stuff with the service proxy
}

// serviceProxy has now been cleaned up

这是确保 IDisposable 变量得到清理的模式。

对于不是一次性的实例,不需要这样做 - 只需让它们超出范围并将其留给 GC。

如果 NotificationServiceProxy 使用大量资源,您需要确保它已正确完成,然后将其制成一次性的,并始终将其包装在 using 或另一个 class 中还实现了 IDisposable。如果不是,那么这个 try-finally 模式就是浪费时间。

不,这不是一个好的做法。

  • 您正在手动完成工作,compiler/garbage 收集器将为您完成。这是不必要的时间花费和不必要的代码混乱,您需要在以后删除或维护。

  • 您完全错过了 .Dispose() 服务参考。尽管此 可以 finally 块中完成,但 using 块正是为此目的而存在的。

这种做法好不好,要看场景。捕获异常通常很好,这样您就可以优雅地处理意外情况。如果您的程序开发了一些其他方法来处理异常情况,那么 Try/Finally 块应该足够了,因为 Catch 块有一些性能损失。

请查看来自 MSDN 的 Consideration When Using Try Catch 文章。

首先,你不需要清除局部变量(垃圾收集器会为你做),但是,它是安全的:

 Object o = new Object(); 
 ...
 o = null; // safe, but not required

你要做的是清除非托管资源;典型的方法是实现 IDisposable 接口并将相应的实例包装到 using:

 // Providing that serviceProxy implements IDisposable
 using (var serviceProxy = new NotificationServiceProxy()) {
   return serviceProxy.GetNotifications(
     userID, 
     request.Filters, 
     request.FilterProperty, 
     usertypeid);
 } // <- here .Net will call serviceProxy.Dispose() and free the resources allocated

有时您必须恢复初始状态,就是这种情况try..finally 设计用于:

  Cursor savedCursor = Cursor.Current;

  try {
    Cursor.Current = Cursors.WaitCursor;
    ... 
  }
  finally {
    // Rain (exception thrown) or shine (no exception), please, restore my cursor
    Cursor.Current = savedCursor;
  }

在您的特定情况下,我看不到任何要恢复的状态,这就是为什么 try..finally 不是应该使用的模式。但是,serviceProxy 很可能会分配一些 非托管资源 (例如,TCP/IP 端口、连接或类似资源),因此您似乎应该实现 IDisposable 对于 NotificationServiceProxy class (如果它还没有实现)并将实例包装到 using.

当您需要 HAS 发生的事情时,无论您的操作是失败还是成功,都可以处理 IDisposable 对象,这很有意义。

try
{
    myDisposable.Do();
}
finally
{
    myDisposable.Dispose(); 
}

你有一个内置的机制可以做到这一点:使用

using(IDisposable myDisposable = new Something())
{
    myDisposable.Do();
}

现在要好好练习了:

这有点取决于您的错误处理策略和清理需求:

  • 如果我有必须完成的事情,无论操作成功还是失败,我都会在 try finally 块中放置。

  • 如果我有一些只需要错误处理而不需要清理的东西,我会在没有 finally 块的情况下放置 try catch

  • 如果我必须进行某种错误处理,我也会在其中放置一个 catch 块

  • 如果我不想像日志那样进行一些错误处理,然后让异常从该堆栈传播,我也会重新抛出异常。

或上述条件的任何排列..

例如:

 try
 {
     myDisposable.Do();
 }
 catch(Exception e)
 {
     Log("Error at something something",e); 
     throw e;
 }
 finally
 {
     myDisposable.Dispose(); 
 }

我个人并没有在每个地方都放置 try catch finally,我希望我的代码中确实包含它们的地方脱颖而出,说明这些操作由于某些已知原因而已知会出错。

我欢迎新的异常,我还不知道并相应地处理它们。