2

在我维护的一些应用程序的许多地方,我发现在一个或句子中使用一个try/finallytry/except块的代码避免使用开始/结束for loopif

考虑下一个代码(不是生产代码,只是一个示例)

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Classes,
  SysUtils;
    
Procedure TestNoBeginEnd;
var
 i   : Integer;
 L1  : TStringList;
begin
    for i := 1 to 10 do
    try
      L1:=TStringList.Create;
      try
        L1.Add('Bar');
        L1.Add(IntToStr(i));
        L1.Add('Foo');
      finally
        Writeln(L1.Text);
        L1.Free;
      end;
    except
      on E: Exception do
        Writeln('Opps '+E.ClassName, ': ', E.Message);
    end;
end;

begin
  try
    TestNoBeginEnd;

  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
  Readln;
end.

问题,在delphi中使用try/finally或try/except代替begin/end是否被认为是一种不好的做法、代码气味或存在任何缺点?

更新

对于愚蠢的示例代码,我很抱歉,只是为了澄清 try/finally 和 try/except 不会假装替换 begin/end ,只是为了避免在使用时使用它(开始/结束) try/finally 或 try/except 不需要开始/结束。

4

4 回答 4

6

我个人认为没有必要begin..end在 a 周围添加额外的内容try..finally/except..end,尤其是tryend.

话虽这么说,问题更多的是我们是否需要系统地在 abegin..end之后..do或者..then即使只有一条指令(在你的情况下是一条try..指令)。

这主要是风格和习惯的问题,有些人更喜欢防御性地编写代码并系统地开始..结束,其他人则尽量避免不必要的行。

当涉及到相当多的行时,我倾向于添加begin..end以使范围更明显。

我想说,使用良好的判断力,看看某人(或您)稍后修改代码的可能性有多大可能会错过预期的范围。

于 2012-01-31T03:22:32.550 回答
1

第一件事。Begin/End 与 Try/Etc 不同。后者首先表示一组指令,而第二个是单个指令(可能包含许多指令)。

所以他们就像一把锤子和梯子。不同工作的不同工具。当然你用梯子钉钉子,但这不是解决问题的最有效方法。

编辑:如果我理解了这个问题(现在您已经对其进行了编辑),您会问是否可以在没有开始/结束的情况下使用一次尝试/等。是的,您可以这样做,因为 try/etc 是一条语句。事实上,我什至会说避免过多的无用代码可能是一件好事。

编辑前:

那是什么意思?好吧,这意味着如果该函数中发生错误,它会被记录下来,但基本上会被忽略。

除非有非常好的异常处理并且代码升级异常或正确恢复,否则我会说这是不好的做法

在您的示例中,所有异常处理程序都会说 Opps(哎呀?)。那有什么好处?当然它可能只是一个示例,因此我会检查代码以了解它的真正作用。我相信写这篇文章的人通常试图处理错误,但实际上使用这种模式使代码的可靠性差得多。

我使用“异常处理程序适用于特殊情况”的一般情况,我可以在其中恢复或者我需要让某人/事物知道。大多数情况下,我发现没有人可以处理错误,并且系统必须按照设计进行降级。这是异常处理程序将安全记录并传递异常的地方。

后一种情况我扩展到所有“系统中的大边界”。即,我将在服务/API 边界收集异常并适当地记录/包装。

(请注意,有时糟糕的 API 会在非异常情况下使用异常,在这种情况下,您可以安全地添加异常处理程序来包装糟糕的设计)。

于 2012-01-31T02:52:49.190 回答
1

您对问题的编辑从根本上改变了问题。您至少应该更改标题,现在我想知道这个问题是否可以挽救(-1)。

也就是说,在解决您最初的问题时:

当 Begin/End 语句就足够时,绝不应使用 Try/Finally 或 Try/Except 语句。除了 Try 语句向编译后的代码添加(微小甚至不明显的)额外指令这一事实之外,它们还暗示了不存在的代码必需品:

for I := 0 to Count - 1 do
try
  Inc(Rect.Left, Shift);
finally
  Inc(Rect.Right, Shift);
end;

只是不要这样做。

于 2012-01-31T06:07:33.840 回答
0

当 try finally/except 为您将相同的代码包装在一个块中时,省略 begin end 并没有错。请记住,try-blocks 比 begin-block 有更多的开销。这种开销通常可以忽略不计,但我发现它在循环中产生有意义的差异,特别是在重复引用的方法中的循环。

考虑到上述细节,您的示例有些糟糕,因为它在循环中反复增加了不必要的开销。每当您可以将 try 块移出循环时,这都是一件好事。在您的示例中,如果您必须能够为每个循环迭代捕获异常,则下面的代码会更有效,如果没有,也将 except 块移出。

Procedure TestNoBeginEnd;
var
 i   : Integer;
 L1  : TStringList;
begin
  L1:=TStringList.Create;
  try
    for i := 1 to 10 do
    try
      L1.Clear;
      L1.Add('Bar');
      L1.Add(IntToStr(i));
      L1.Add('Foo');
      Writeln(L1.Text);
      end;
    except
      on E: Exception do
        Writeln('Opps '+E.ClassName, ': ', E.Message);
    end;
  finally
    L1.Free;
  end;
end;
于 2012-01-31T18:46:15.167 回答