代码之家  ›  专栏  ›  技术社区  ›  Tom A RRUZ

Delphi异常处理-如何正确清理?

  •  10
  • Tom A RRUZ  · 技术社区  · 16 年前

    我正在看我们的一个应用程序中的一些代码,遇到了一些与我通常所做的有点奇怪的事情。对于异常处理和清理,我们(和其他程序员一样,我确信)使用Try/Finally块嵌入Try/Except块。现在我习惯了Try/Except在Try/Finally里面这样:

    Try
      Try
        CouldCauseError(X);
      Except
        HandleError;
      end;
    Finally
      FreeAndNil(x);
    end;
    

    但另一段代码则相反:

    Try
      Try
        CouldCauseError(X);
      Finally
        FreeAndNil(x);
      end;
    Except
      HandleError;
    end;
    

    环顾网络,我看到人们这样做的两种方式,没有解释为什么。我的问题是,哪一个得到外边的块和哪一个得到内边的块有关系吗?或者,except和finally部分是否会得到处理,而不管它是以何种方式构造的?谢谢

    4 回复  |  直到 16 年前
        1
  •  5
  •   Deltics    16 年前

    一个区别是try..finally..except可能容易受到异常屏蔽情况的影响。

    假设在 可能导致错误() . 想象一下 自由零 (十) 在 最后 导致另一个异常。最初的异常(很可能导致不稳定导致 自由零 除了 处理程序现在正在处理在原始异常之后发生的“下游”异常。

    尝试..除了..最后 当然要避免这种情况,因此应该优先考虑(处理尽可能接近其来源的异常)。

    另一种处理此类简单情况(单个对象正在清理)的方法是在正常流中同时包含清理 在异常处理程序中:

    try
      CouldCauseError(X);
      FreeAndNil(x);
    except
      HandleError;
      FreeAndNil(x);
    end;
    

    一开始看起来有点吓人(“我需要确定 (十) 所以我必须有一个 !!") 但是不调用第一个FreeAndNIL()的唯一方法是如果有异常 不管怎样,一个异常也是FreeAndNIL(),它使得异常情况下的清理顺序更加清晰(从某种意义上说,为了理解发生了什么,在某种程度上必须“过滤”掉噪声)。

    然而,这取决于以下事实: ()实际上是“ NILThenFree公司 ()"... 在释放之前是NIL'd,所以如果 自由零 (十) 在正常流中,当异常处理程序捕获由引发的异常时,X将为NIL ,因此它不会尝试“双重释放”X。

    不管你决定什么,我希望这会有帮助。

        2
  •  4
  •   The_Fox    16 年前

        3
  •  2
  •   Francesca    16 年前

    这完全取决于finally块中的代码是否能够引发异常本身(然后需要 ),或者如果在异常处理中需要稍后释放的内容(那么需要 ).

    这意味着有时你甚至可以有一些代码,比如:

      try
        try
          try
            CouldCauseError(X);
          except
            HandleErrorWith(X);
          end;
        finally
          FreeAndNil(X); // and/or any resource cleanup
        end;
      except
        CatchAllError;
      end;
    
        4
  •  0
  •   Heinz Z.    16 年前

    X := CreateAnX
    try
      DoSomeThing(X);
    finally
      FreeAndNil(x);
    end;
    

    这很重要。因为如果你有这样的代码

    // don't do something like this
    try
      X := CreateAnX
      DoSomeThing(X);
    finally
      FreeAndNil(x);
    end;
    

    你可以的 幸运的 而且很有效。但如果施工失败,你可以 幸运的 “并得到一个访问违规或你有 然后在完全不同的代码位置获取访问冲突。

    另一种选择是

    X := nil;
    try
      X := CreateAnX
      DoSomeThing(X);
    finally
      FreeAndNil(x);
    end;
    

    在哪里使用 除了 取决于你的意图。当您想捕获每个异常并知道所有调用代码都会清除它的问题(使用try finally)时,就可以使用outer except块

    try
      X := CreateAnX
      try
        DoSomeThing(X);
      finally
        FreeAndNil(x);
      end;
    except
      on e: Exception do
        LogException(e)
    end;      
    

    但当你想抓住所有的错误时,一定要考虑一下。作为一个例子(我经常认为这是错误的),不要在Indy OnExecute处理程序中这样做。你一定要用这样的东西

    try
      X := CreateAnX
      try
        DoSomeThing(X);
      finally
        FreeAndNil(x);
      end;
    except
      on EIdException do
        raise;
      on e: Exception do
        LogException(e)
    end;      
    

    如果由于抛出异常或(例如)会话可能失败而预期出现异常,请查找最内部的位置以捕获错误:

    X := CreateAnX
    try
      DoSomeThing(X);
      try
        i := StrToInt(X.Text);
      except
        on EConvertError do
          i := 0;
      end;       
    finally
      FreeAndNil(x);
    end;
    

    别这样做

    X := CreateAnX
    try
      try
        DoSomeThing(X);
        i := StrToInt(X.Text);
      except
        on EConvertError do
          i := 0;
      end;       
    finally
      FreeAndNil(x);
    end;
    

    只有当你预期剂量测量也会出现误差时才这样做。如果DoSomeThing抛出了一个EConvertError,并且您没有预料到它,那么您的代码就有一个需要更正的严重问题。在这种情况下,请确保用户可以保存他的工作(可能作为副本,因为他的工作可能会被损坏),并确保您获得有关问题的信息。

    至少try except是为了处理异常,而不是为了隐藏异常。