在Delphi 7中,`try ... except raise; end;`是否有意义?

8

我正在维护一些 Delphi 7 代码,发现有很多如下的实例:

with ADOQuery1 do begin
  // .. fill out sql.text, etc
  try
    execSQL;
  except
    raise;
  end;
end;

我认为这些try块可以被移除,因为它们没有做任何事情。然而,我担心可能会有微妙的副作用。

有人能想到这些块实际上可能会发挥作用的情况吗?如果没有它们,是否会发生任何事情?


另请参阅:https://dev59.com/r3RB5IYBdhLWcg3wHkLi - Rob Kennedy
8个回答

10

在这种情况下,raise操作没有任何作用,应该被移除,因为它只是重新抛出了异常块刚刚捕获的异常。当没有适当的错误处理可用时,raise通常用于将控制转移到块的末尾。在接下来的代码中,我们处理自定义异常,但是任何其他异常都应该在其他地方进行处理。

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;

要注意的是,有一种类似的表单没有使用"raise",但确实有隐藏任何异常的副作用。这是非常不道德的开发人员使用的做法,他们希望已经转向竞争对手的职位。

with ADOQuery1 do begin  
  // .. fill out sql.text, etc  
  try    
    execSQL; 
  except
    // no handler so this just eats any "errors"    
  end;

3
Mike,你的第一个示例代码不需要使用“raise”。任何非 ECustomException 类型的异常都会自动传播到下一个处理程序。只需省略整个“else”部分即可。 - Rob Kennedy
你说得对。这只是出于习惯而已。唯一需要使用else raise的情况是如果你有其他要执行的代码,那么它就是必需的。我年纪大了,开始生疏了。 - MikeJ

6

在上面的代码段中删除except代码不会有任何影响。你可以(我相信你应该,因为它降低了可读性)将其删除。


放弃这样的代码还有一个很好的理由:在调试时,如果出现异常,您不想需要逐个步骤地查看所有这些代码。 - Stijn Sanders

2
我可能回答得有些匆忙,看看结尾...
就像它现在的状态一样,对应用程序来说是毫无用处的。
就这样!

现在来说“为什么”。如果在引发异常之前插入了某种日志记录代码,则可能是为了标准化异常处理方式,以便在其他位置(过去、将来或现在)出现时能够处理。
  try
    execSQL;
  except
    // Log Exception..
    on E: Exception do
    begin
      LogTrace(Format('%s: Exception Message[%s]',[methodname, E.Message]));
      raise;
    end;
  end;

或者用于清理代码:

  try
    execSQL;
  except
    //some FreeAndNil..
    raise;
  end;
更新: 有1种情况我会使用原文...为了能在raise行上设置断点,以便有机会查看该代码块的上下文中发生了什么。

2
第二个是完全不符合惯用语的,应该使用finally。这样的东西实际上会降低代码库的可读性,所以修复它也是一个原因。 - mghie
2
用 Connection.Rollback 替换 FreeAndNil。 - Blorgbeard
没错,Blorgbeard。只有在出现问题时才需要进行任何类型的维护工作,然后将异常向上冒泡。 - Francesca
是的,你说得对。从评论中我并没有看出FreeAndNil()是有条件的,但现在我明白了。 - mghie
3
在这种情况下设置断点似乎有些愚蠢,因为引发异常会将您发送到调试器。 - Mason Wheeler

2

好的,这里有两个问题。

首先,它是有意义的:如果execSQL抛出异常,它会被try块捕获并转发到except。然后通过raise将其转发到下一个更高的块。

第二,它是否有用?可能不是。它几乎肯定是以下三种情况之一的结果:

  1. 头发竖起来的人编写了一份编码标准,其中规定“所有可能引发异常的操作都必须在try块中”。
  2. 有人打算回来将execSQL语句产生的异常转换为其他更有意义的异常。
  3. 新手不知道他们所写的与让外部环境处理异常同构,因此认为他们必须转发它。

让我担心的是,我知道谁写了这段代码,而且我不认为1-3适用于这里。除了可能是第三个.. 嗯。 - Blorgbeard
哦,他已经离开了,所以我不能问他了。 - Blorgbeard
这个人以前是否用过Java编程?也许他认为必须捕获异常,所以没有尝试其他方法。 - malach
在Java中,并不是所有的异常都需要被捕获,只有“已检查异常”需要。 - mjn
@mjustin,我认为我们都同意这是一个对异常使用有误解的人。 - Charlie Martin

2

这段代码除了让原始程序员在“Raise”处设置断点并更接近异常可能的原因之外,没有其他作用。从这个意义上说,这是一种完全合理的调试技术。


1

实际上,我应该将这个作为评论发布到François的回答中,但我不知道是否可以在那里插入格式化的代码 :( 所以我将其发布为答案。

2mghie:

第二个完全不符惯用法,应该使用finally。

不,“finally”会始终清理对象。“Except”-只有在异常情况下才会清理。考虑一个创建、填充并返回对象的函数的情况:

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,但是...那不是很丑吗?

我同意,可以看看我对Francois回答的评论。 - mghie

0

标题包含了一个相当广泛的问题,而它的解释则给出了一个更具体的例子。因此,我的回答关于如何从这个例子中进行处理,可能无法为已经在这里说过的内容添加任何有用的东西。

但是,也许 Blorgbeard确实想知道是否有意义去使用 try ... except raise; end。在Delphi 7中,如果我记得正确的话,Exit会触发try-finally块的finally部分(就像它是某种异常一样)。有些人可能认为这种行为不适合他们的任务,并且使用所讨论的结构是一个相当棘手的解决方法。

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


最后的代码块(finally)会在所有情况下执行,无论是否出现异常,因此这不可能是原因。 - Blorgbeard

0

这段代码除了重新抛出一个本来就会在没有该try except块的情况下抛出的异常外,什么也不做。你可以安全地将其删除。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接