关于异常:在delphi 7中,是`try …除了raise;

In delphi 7, is `try … except raise; end;` meaningful at all?

在我维护的一些Delphi7代码中,我注意到了以下许多实例:

1
2
3
4
5
6
7
8
with ADOQuery1 do begin
  // .. fill out sql.text, etc
  try
    execSQL;
  except
    raise;
  end;
end;

在我看来,这些尝试块可以被删除,因为它们什么都不做。不过,我对可能出现的细微副作用持谨慎态度。

有人能想到任何一种情况,在这种情况下,如果没有这些块,这些块实际上可以做任何不可能发生的事情吗?


在此上下文中,引发操作没有效果,应将其移除,因为它只是简单地重新引发异常块刚刚捕获的异常。提升通常用于在没有适当的错误处理可用时将控制转移到块的末尾。在下面我们将处理自定义异常,但任何其他异常都应在其他地方处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
try
  someOperation;
except
  on e: ECustomException do
    SomeCustomHandelr;
  else
     begin
       // the raise is only useful to rethrow the exception to an encompasing
       // handler.  In this case after I have called my logger code. as Rob
       // mentioned this can be omitted if you arent handling anything because
       // the compiler will simply jump you to the next block if there is no
       // else.
       LogUnexpectedException('some operation failed',e);
       raise;
     end;
end;

注意,在没有"加薪"的情况下,有相似的外形,这确实会产生吃/隐藏任何例外情况的副作用。非常不道德的开发人员的实践,他们希望在竞争中继续占据一席之地。

1
2
3
4
5
6
7
with ADOQuery1 do begin  
  // .. fill out sql.text, etc  
  try    
    execSQL;
  except
    // no handler so this just eats any"errors"    
  end;


删除上面代码段中的except代码没有任何区别。你可以(我相信你应该这样做,因为这会降低可读性)删除它。


除了允许原始程序员在"引发"上放置一个断点,并在源代码中看到异常更接近其可能原因之外,此代码什么也不做。从这个意义上说,它是一种非常合理的调试技术。


我可能回答得有点快,看最后…就像它一样,它对应用程序是无用的。时期!

现在站在"为什么"的一边。如果存在/曾经/将来/现在/正在其他地方/在提升前插入某种日志代码,则可以标准化异常处理:

1
2
3
4
5
6
7
8
9
10
  try
    execSQL;
  except
    // Log Exception..
    on E: Exception do
    begin
      LogTrace(Format('%s: Exception Message[%s]',[methodname, E.Message]));
      raise;
    end;
  end;

或清除代码:

1
2
3
4
5
6
  try
    execSQL;
  except
    //some FreeAndNil..
    raise;
  end;

更新:有一种情况下,我会看到一些使用,就像它是……为了能够在raise行上放置一个断点,以便有机会了解在该代码块的上下文中发生了什么。


好吧,这里有两个问题。

首先,它是有意义的:如果execsql抛出异常,它会被try块捕获并转发到except。然后它被提升转发到下一个更高的块。

第二,它有用吗?大概不会。几乎可以肯定,这是三件事之一的结果:

  • 有个头发尖尖的人写了一个编码标准,上面说"所有可以抛出异常的操作都必须在一个try块中。"
  • 有人打算回来把execSQL语句中的异常转换成另一个更有意义的异常。
  • 一个新来的人不知道他们写的是同构的,让子宫环境担心这个例外,所以认为他们必须转发它。

  • 事实上,我应该把这个作为评论贴给弗兰?OIS的答案,但我不知道是否可以在那里插入格式化代码:(所以我把这个作为答案发布。

    2MGHIE:

    The second one is completely unidiomatic, one would use finally instead.

    < /块引用>< /块引用>

    不,"finally"将始终清除对象。例外"—只有例外。考虑函数的情况,它创建、填充和返回一个对象:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    function CreateObj: TSomeObj;
    begin
      Result := TSomeObj.Create;
      try
        ... // do something with Result: load data, fill props, etc.
      except
        FreeAndNil(Result); // oops: bad things happened. Free object to avoid leak.
        raise;
      end;
    end;

    如果您将"finally"放在这里-函数将始终返回nil。如果您完全忽略了"try"块,那么在"…"中出现异常时,将出现资源泄漏。

    当然,你可以用"finally"检查exceptobj,但是…不是很难看吗?


    除了重新引发一个异常外,此代码什么也不做,该异常将在没有此try-except块的情况下完全就绪地引发。您可以安全地将其移除。


    标题包含了相当广泛的问题,而它的解释给出了一个更具体的例子。所以,我对这个问题的回答,如它是如何从这个例子中得到的,可以毫无疑问地为这里所说的内容增加任何有用的内容。

    但是,也许布罗格比尔德真的想知道这对try ... except raise; end是否有意义。在Delphi7中,如果我记得正确的话,Exit会触发try-finally块的finally部分(好像它是某种异常)。有人可能认为这样的行为不适合他们的任务,而使用所讨论的结构是一个很好的解决方法。

    只是在那里使用一个单独的raise;仍然是很奇怪的,但是正如查理所观察到的那样,我们应该讨论有用性而不是意义性。