DELPHI - except 块中的额外分号,有什么用吗? except 或 end 之前的可选分号,有或没有哪个更好?

DELPHI - Extra semi-colons in except blocks, any purpose? Optional semi-colon before except or end, What's better with or without?

我正在使用 Delphi 10.3.3 中存在一些分号异常的应用程序。我一直在阅读一些分号是可选的,具体取决于编译器,而不是在语句之​​间。多年来,我一直认为这是 Delphi 编译器允许的错误。如果应用程序在任何块结束语句之前被删除,我可以看到应用程序编译。

[except] 和 [end] 之间的分号是否用于代码中的任何目的,例如更快的 运行 时间或抛出一些默认异常,或其他目的?这会引发任何随机编译或 运行 时间错误吗?

在结尾或 except 之前的最后一行留下分号会加快 运行时间吗?一种方式比另一种方式更容易出错,或者更容易出现随机编译或 运行 时间错误?

这里是可以拖放到任何测试项目中的代码,其中的表单具有编辑框、按钮和使用拖放默认设置创建的备注框。

function TForm1.isThisUseful(this : string): boolean;
var
  i: Integer;
begin
  result := false;
  try
    if this = ''
    then
      i := 0
    else
      i := 1
  except
    ;
  end;
  result := (i > 0)
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
  if isThisUseful(Edit1.Text)
  then
    Memo1.Text := 'Yes, it is useful'
  else
    Memo1.Text := 'This is a waste of time.'
end;

tl;dr:没有任何区别。

try
  // do stuff
except
  ;
end;

这个分号可以去掉。它绝对没有任何作用。它没有好处,也没有坏处(除了可能会使源代码看起来很奇怪,您应该避免这种情况1)。

begin
  DoThis;
  DoThat;
end;

由于在 Delphi 中,分号 分隔 语句,所以 end 之前的最后一个分号不是必需的。它绝对没有任何作用。喜欢不喜欢主要是个人喜好问题(我个人是有时吃有时不吃)。

使用这个分号的一个好处可能是您可以将此行不加修改地移动或复制到另一个地方,在那里它不是它的最后一条语句堵塞。同样,您也可以在这行代码之后直接插入一条新语句,而无需添加分号。恕我直言,这些都是非常的小好处;如果有必要,添加分号并不难。

奖金评论: 但是,result := (i > 0) 对我来说确实很奇怪。我期望 Result := i > 0,没有不必要的括号。 (这可能是因为 20 多年来我几乎每天都在 Delphi 编程。)

奖励评论 2: 注意编译器很乐意接受

begin
  DoThis;;
  DoThat;
end;

还有。它只是忽略多余的分号。但是上面的代码片段在我看来 非常 难看,而且我总是觉得开发人员不先阅读代码就选择提交代码很奇怪。

奖励评论 3: Ken Bourassa 写了一个 给出了分号存在或不存在的场景示例 [= =54=]影响程序。但还有一个更可怕的例子:

case i of
  0:
    DoA;
  1:
    DoB;
  2:
    if j = 0 then
      DoC{;}
else
  DoD;
end;

1 在这种情况下,您还可以争辩说分号有一个用途:它 "highlights" 空的 except 块,使其稍微容易一些快速发现。因此,这可能是故意的。但有人可能会争辩说,突出显示它的更好方法是包含像 // Do nothing// Just carry on 这样的注释。 (而且,在很多情况下,如果不是大多数情况,空 except 块根本不好。)

除了安德烈亚斯的回答...

有 1 种情况你肯定(或者,至少,最有可能)不想留下分号,它在 "then" 之后。下面的代码会在消息框中显示"B"

procedure TForm3.FormCreate(Sender: TObject);
var
  S : String;
begin
  S := 'A';
  if 1 = 2 then;
    S := 'B';

  ShowMessage(S);
end;

据我所知,这是插入分号会自行更改代码生成的唯一情况。

我想我会尝试一些测量,在这个 D7 测试程序上使用 NexusDB's Quality Suite 的线路定时器:

{$A8,B-,C+,D+,E-,F-,G+,H+,I+,J-,K-,L+,M-,N+,O-,P+,Q-,R-,S-,T-,U-,V+,W+,X+,Y+,Z1}
program semicolontest;

{$APPTYPE CONSOLE}

uses
  SysUtils;

function isThisUseful1(this : string): boolean;
var
  i: Integer;
begin
  result := false;
  try
    if this = ''
    then
      i := 0
    else
      i := 1
  except
    ;
  end;
  result := (i > 0)
end;

function isThisUseful2(this : string): boolean;
var
  i: Integer;
begin
  result := false;
  try
    if this = ''
    then
      i := 0
    else
      i := 1
  except

  end;
  result := (i > 0)
end;

var
  Input : String[20];
  i : Integer;
  Res : Boolean;

begin

   Input := 'abcdefg';

   writeln('Starting');
   for i := 1 to 1000000 do begin
     Res := isThisUseful1(Input);
     Res := isThisUseful2(Input);
   end;
   writeln('Done');
   // readln;
end.

其中包含问题中 isThisUseful 函数的两个版本,一个在 except 块中有分号,一个没有分号。 运行 这段代码的结果(对布局略有损坏表示歉意)如下:

Line    Total Time  Hit Count   Source
1       -   {$A8,B-,C+,D+,E-,F-,G+,H+,I+,J-,K-,L+,M-,N+,O-,P+,Q-,R-,S-,T-,U-,V+,W+,X+,Y+,Z1}
2       -   program semicolontest;
3       -   
4       -   {$APPTYPE CONSOLE}
5       -   
6       -   uses
7       -     SysUtils;
8       -   
9       -   function isThisUseful1(this : string): boolean;
10      -   var
11      -     i: Integer;
12  2.807175    1,000,000   begin
13  0.761388    1,000,000     result := false;
14  2.133202    1,000,000     try
15  1.403061    1,000,000       if this = ''
16      -       then
17      -         i := 0
18      -       else
19  0.826841    1,000,000         i := 1
20      -     except
21      -       ;
22      -     end;
23  0.735419    1,000,000     result := (i > 0)
24      -   end;
25      -   
26      -   function isThisUseful2(this : string): boolean;
27      -   var
28      -     i: Integer;
29  1.260030    1,000,000   begin
30  0.782293    1,000,000     result := false;
31  0.894420    1,000,000     try
32  0.820860    1,000,000       if this = ''
33      -       then
34      -         i := 0
35      -       else
36  0.873424    1,000,000         i := 1
37      -     except
38      -   
39      -     end;
40  0.778922    1,000,000     result := (i > 0)
41      -   end;
42      -   
43      -   var
44      -     Input : String[20];
45      -     i : Integer;
46      -     Res : Boolean;
47      -   
48  1.167140    1   begin
49      -   
50  0.000036    1      Input := 'abcdefg';
51      -   
52  0.641347    1      writeln('Starting');
53  0.000026    1      for i := 1 to 1000000 do begin
54      -        Res := isThisUseful1(Input);
55      -        Res := isThisUseful2(Input);
56  0.669066    1,000,000      end;
57  0.402661    1      writeln('Done');
58      -      // readln;
59      -   end.
60      -   

请注意,第 21 行(try ...except 块中的分号)没有记录时间,原因很简单,正如预期的那样,没有为其生成代码。

为了完整性,以下是isThisUseful1isThisUseful2

的反汇编

isThisUseful1:

Address Bytes   Text    Block #
00408620h       [semicolontest.dpr.12] begin    
00408620h   55        push ebp  1
00408621h   8BEC          mov ebp, esp  1
00408623h   83C4F4        add esp, 0FFFFFFF4h ; (-0Ch)  1
00408626h   53        push ebx  1
00408627h   56        push esi  1
00408628h   57        push edi  1
00408629h   8945FC        mov [ebp-04h], eax    1
0040862Ch   8B45FC        mov eax, [ebp-04h]    1
0040862Fh   E87CB7FFFF        call System::LStrAddRef ;(00403DB0h)  1
00408634h   33C0          xor eax, eax  1
00408636h   55        push ebp  1
00408637h   689A864000        push offset @@6   1
0040863Ch   64FF30        push fs:[eax] 1
0040863Fh   648920        mov fs:[eax], esp 1
00408642h       [semicolontest.dpr.13] result := false; 
00408642h   C645FB00          mov byte ptr [ebp-05h], 00h   1
00408646h       [semicolontest.dpr.14] try  
00408646h   33C0          xor eax, eax  1
00408648h   55        push ebp  1
00408649h   6872864000        push offset @@3   1
0040864Eh   64FF30        push fs:[eax] 1
00408651h   648920        mov fs:[eax], esp 1
00408654h       [semicolontest.dpr.15] if this = '' 
00408654h   837DFC00          cmp dword ptr [ebp-04h], 00h  1
00408658h   7507          jnz @@1   1
0040865Ah       [semicolontest.dpr.17] i := 0   
0040865Ah   33C0          xor eax, eax  2
0040865Ch   8945F4        mov [ebp-0Ch], eax    2
0040865Fh   EB07          jmp @@2   2
00408661h       [semicolontest.dpr.19] i := 1   
00408661h       @@1:    
00408661h   C745F401000000        mov dword ptr [ebp-0Ch], 01h  3
00408668h       @@2:    
00408668h   33C0          xor eax, eax  4
0040866Ah   5A        pop edx   4
0040866Bh   59        pop ecx   4
0040866Ch   59        pop ecx   4
0040866Dh   648910        mov fs:[eax], edx 4
00408670h   EB0A          jmp @@4   4
00408672h       @@3:    
00408672h   E9DDACFFFF        jmp System::HandleAnyException ;(00403354h)   5
00408677h       [semicolontest.dpr.21] ;    
00408677h   E8B8AEFFFF        call System::DoneExcept ;(00403534h)  5
0040867Ch       [semicolontest.dpr.23] result := (i > 0)    
0040867Ch       @@4:    
0040867Ch   837DF400          cmp dword ptr [ebp-0Ch], 00h  6
00408680h   0F9F45FB          setnle [ebp-05h]  6
00408684h   33C0          xor eax, eax  6
00408686h   5A        pop edx   6
00408687h   59        pop ecx   6
00408688h   59        pop ecx   6
00408689h   648910        mov fs:[eax], edx 6
0040868Ch   68A1864000        push offset @@7   6
00408691h       @@5:    
00408691h   8D45FC        lea eax, [ebp-04h]    7
00408694h   E8BFB3FFFF        call System::LStrClr ;(00403A58h) 7
00408699h   C3        ret   7
0040869Ah       @@6:    
0040869Ah   E9E1ADFFFF        jmp System::HandleFinally ;(00403480h)    8
0040869Fh   EBF0          jmp @@5   8
004086A1h       @@7:    
004086A1h   8A45FB        mov al, [ebp-05h] 9
004086A4h       [semicolontest.dpr.24] end; 
004086A4h   5F        pop edi   9
004086A5h   5E        pop esi   9
004086A6h   5B        pop ebx   9
004086A7h   8BE5          mov esp, ebp  9
004086A9h   5D        pop ebp   9
004086AAh   C3        ret   9

isThisUseful2:

Address Bytes   Text    Block #
00408620h       [semicolontest.dpr.12] begin    
00408620h   55        push ebp  1
00408621h   8BEC          mov ebp, esp  1
00408623h   83C4F4        add esp, 0FFFFFFF4h ; (-0Ch)  1
00408626h   53        push ebx  1
00408627h   56        push esi  1
00408628h   57        push edi  1
00408629h   8945FC        mov [ebp-04h], eax    1
0040862Ch   8B45FC        mov eax, [ebp-04h]    1
0040862Fh   E87CB7FFFF        call System::LStrAddRef ;(00403DB0h)  1
00408634h   33C0          xor eax, eax  1
00408636h   55        push ebp  1
00408637h   689A864000        push offset @@6   1
0040863Ch   64FF30        push fs:[eax] 1
0040863Fh   648920        mov fs:[eax], esp 1
00408642h       [semicolontest.dpr.13] result := false; 
00408642h   C645FB00          mov byte ptr [ebp-05h], 00h   1
00408646h       [semicolontest.dpr.14] try  
00408646h   33C0          xor eax, eax  1
00408648h   55        push ebp  1
00408649h   6872864000        push offset @@3   1
0040864Eh   64FF30        push fs:[eax] 1
00408651h   648920        mov fs:[eax], esp 1
00408654h       [semicolontest.dpr.15] if this = '' 
00408654h   837DFC00          cmp dword ptr [ebp-04h], 00h  1
00408658h   7507          jnz @@1   1
0040865Ah       [semicolontest.dpr.17] i := 0   
0040865Ah   33C0          xor eax, eax  2
0040865Ch   8945F4        mov [ebp-0Ch], eax    2
0040865Fh   EB07          jmp @@2   2
00408661h       [semicolontest.dpr.19] i := 1   
00408661h       @@1:    
00408661h   C745F401000000        mov dword ptr [ebp-0Ch], 01h  3
00408668h       @@2:    
00408668h   33C0          xor eax, eax  4
0040866Ah   5A        pop edx   4
0040866Bh   59        pop ecx   4
0040866Ch   59        pop ecx   4
0040866Dh   648910        mov fs:[eax], edx 4
00408670h   EB0A          jmp @@4   4
00408672h       @@3:    
00408672h   E9DDACFFFF        jmp System::HandleAnyException ;(00403354h)   5
00408677h       [semicolontest.dpr.21] ;    
00408677h   E8B8AEFFFF        call System::DoneExcept ;(00403534h)  5
0040867Ch       [semicolontest.dpr.23] result := (i > 0)    
0040867Ch       @@4:    
0040867Ch   837DF400          cmp dword ptr [ebp-0Ch], 00h  6
00408680h   0F9F45FB          setnle [ebp-05h]  6
00408684h   33C0          xor eax, eax  6
00408686h   5A        pop edx   6
00408687h   59        pop ecx   6
00408688h   59        pop ecx   6
00408689h   648910        mov fs:[eax], edx 6
0040868Ch   68A1864000        push offset @@7   6
00408691h       @@5:    
00408691h   8D45FC        lea eax, [ebp-04h]    7
00408694h   E8BFB3FFFF        call System::LStrClr ;(00403A58h) 7
00408699h   C3        ret   7
0040869Ah       @@6:    
0040869Ah   E9E1ADFFFF        jmp System::HandleFinally ;(00403480h)    8
0040869Fh   EBF0          jmp @@5   8
004086A1h       @@7:    
004086A1h   8A45FB        mov al, [ebp-05h] 9
004086A4h       [semicolontest.dpr.24] end; 
004086A4h   5F        pop edi   9
004086A5h   5E        pop esi   9
004086A6h   5B        pop ebx   9
004086A7h   8BE5          mov esp, ebp  9
004086A9h   5D        pop ebp   9
004086AAh   C3        ret   9

(我一直在斗鸡眼试图自己比较这些,并且不会浪费我的时间 运行 他们过去了 BeyondCompare)。

无论如何,我认为这表明正如我自己和其他人所说,编译器不会为 try...except 块中仅包含分号的行生成代码,并且它不应该在其他任何地方生成代码,要么,尽管如前所述,在某些情况下,分号的存在或其他方式会改变代码的语义,从而改变生成的代码。

感谢所有对此 post 的反馈。我想总结几点..

分号改变了应用程序的编译方式。因此,应用程序的大小可能会发生微小的变化。因此,它似乎是应用程序中的硬或分支开关终结器。也就是说,它可能应该在每个结束时 尝试除了结束;在每个 except 或 finally 之前,以及在每个循环的结尾,无论后面是什么,以确保编译是干净的并且始终如一。

但不应该是另一个 [end] 语句之前的 if-[end] 中遗漏的问题,或者作为不存在其他代码的地方的附加项目。但可能在循环内多一个分号,并不是最好的主意。