'价值从未使用'更改为'变量未初始化'

时间:2016-09-02 12:51:59

标签: delphi compiler-warnings delphi-10-seattle

如果我编译下面的代码,它会在第1行发出警告:

  分配给lTime的

值从未使用*

但如果删除该行,我会在第2行收到不同的警告:

  

变量lTime可能尚未初始化

编译器是否缺少某些内容,或者是我?

procedure TFormWebServices.RemoveOldReports;
var
  TSR       : TSearchRec;
  I         : Integer;
  lCutOff,
  lTime     : Int64;
  TSToDelete: TStringList;
  S,Msg     : String;
  E         : Exception;
begin
  lCutOff := DelphiToJavaDateTime(Now - cDefReportLifeMins/1440);
  I := FindFirst(FReportDir + '*.pdf',0,TSR);
  TSToDelete := TStringList.Create;
  while I = 0 do
  begin
    if (TSR.Attr and faDirectory) = 0 then 
    begin
      lTime := lCutOff;          // Line 1
      try
        lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1));
      except
        on E:Exception do lTime := lCutOff;
      end;
      if lTime < lCutOff then    // Line 2
        TSToDelete.Add(TSR.Name);
    end;
    I := FindNext(TSR);
  end;

这不是Why is the Compiler warning that variable may not be initialized?的欺骗,因为我也在例外中指定lTime

2 个答案:

答案 0 :(得分:7)

lTime := lCutOff;       
try
  lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1));
except
  on E:Exception do lTime := lCutOff;
end;
if lTime < lCutOff then
  TSToDelete.Add(TSR.Name);

编译器警告此代码是正确的。在第一行分配lTime := lCutOff时,永远不会读取该分配中写入的值。

但是,当您删除代码时,事情就不那么明确了。

try
  lTime := StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1));
except
  on E:Exception do lTime := lCutOff;
end;
if lTime < lCutOff then
  TSToDelete.Add(TSR.Name);

有两种情况需要考虑:try/except块内未引发异常,或者引发异常。

如果没有引发异常,那么很简单,lTime被赋予StrToInt64的结果,因此在被读取之前被初始化。

在引发异常时,lTime未在块内初始化。接下来会发生什么?

  • 如果异常来自Exception(非强制性),则会被捕获并初始化lTime
  • 如果异常不是来自Exception,那么它就不会被捕获,并且永远不会读取lTime

因此,编译器可以推断所有这些,因此不会发出未初始化的变量警告。但是,遗憾的是,编译器不会对这种复杂性进行流程分析。我相信它的逻辑运行如下:

  1. lTime初始化之前引发异常,然后
  2. 可以捕获异常,在这种情况下
  3. 然后读取变量lTime,但仍然可能未初始化。
  4. 在第2步中,它应该能够意识到lTime已初始化,但它根本不执行此类分析。因此,虽然有人可能会认为编译器可以做得更好,但您必须接受这一点作为其分析算法的限制。

    了解到我们的任务是找到编写代码的方法并避免警告。我们不想压制警告,我们只需要找到一种方法来编写代码,使其既正确又无警告。

    在我看来,前进的方向是认识到异常是在这里使用的错误工具。此转换失败是正常行为。您应该使用转换函数编写代码,这些函数在失败时不会引发异常。例如,您可以使用以下选项之一:

    if TryStrToInt64(..., lTime) then
      if lTime < lCutOff then
        ....
    else
      lTime := lCutoff;
    

    或者:

    lTime := StrToInt64Def(..., lCutoff);
    if lTime < lCutOff then
      ....
    

答案 1 :(得分:-1)

这是正确的行为。第1行的值永远不会被使用,因为在try / except中你分配一个新的值而不使用前一个值,因此警告。

如果删除该行,则尝试在try / except块之外使用lTime变量,该变量可能会失败,而不会将值实际设置为lTime

procedure TFormWebServices.RemoveOldReports;
var
  TSR       : TSearchRec;
  I         : Integer;
  lCutOff,
  lTime     : Int64;
  TSToDelete: TStringList;
  S,Msg     : String;
  E         : Exception;
   begin
   lCutOff := DelphiToJavaDateTime(Now - cDefReportLifeMins/1440);
   I := FindFirst(FReportDir + '*.pdf',0,TSR);
   TSToDelete := TStringList.Create;
   while I = 0 do
      begin
         if (TSR.Attr and faDirectory) = 0 then 
            begin
               lTime := lCutOff;          // Line 1
               try
                  lTime :=     StrToInt64(Copy(TSR.Name,1,pos('.',TSR.Name)-1));
               except
                  on E:Exception do lTime := lCutOff;
               end;
               if lTime < lCutOff then    // Line 2
                  TSToDelete.Add(TSR.Name);
            end;
         I := FindNext(TSR);
      end;

你可以这样做

procedure TFormWebServices.RemoveOldReports;
var
  TSR       : TSearchRec;
  I         : Integer;
  lCutOff,
  lTime     : Int64;
  TSToDelete: TStringList;
  S,Msg     : String;
  E         : Exception;
   begin
   lCutOff := DelphiToJavaDateTime(Now - cDefReportLifeMins/1440);
   I := FindFirst(FReportDir + '*.pdf',0,TSR);
   TSToDelete := TStringList.Create;
   while I = 0 do
      begin
         if (TSR.Attr and faDirectory) = 0 then 
            begin
               lTime := StrToInt64Def(Copy(TSR.Name,1,pos('.',TSR.Name)-1), lCutOff);
               if lTime < lCutOff then    
                  TSToDelete.Add(TSR.Name);
            end;
         I := FindNext(TSR);
      end;