2016-09-02 5 views
4

Если я скомпилировать код ниже он дает предупреждение на линии 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 в исключение, а также.

+1

Возможный дубликат (HTTP: // StackOverflow. com/questions/11554857/why-is-the-compiler-warning-that-variable-may-not-be-initialized) –

+1

Это похоже на дубликат. –

+0

На самом деле, здесь нет необходимости использовать обработку исключений. RTL предоставляет набор методов TryStrTo [x] для этой цели - гораздо эффективнее, чем позволить обработчику исключений справиться с этим. [Документация TryStrToInt64] (http://docwiki.embarcadero.com/Libraries/rus/System.SysUtils.TryStrToInt64) –

ответ

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 затем считывается, и по-прежнему может быть неинициализированным.

На этапе 2 он должен понимать, что инициализирован lTime, но он просто не выполняет такой анализ. Поэтому, хотя можно утверждать, что компилятор может сделать лучше, вам просто нужно принять это как ограничение его алгоритмов анализа.

Поняв, что нам остается задача найти способ написать код и избежать предупреждений. Мы не хотим подавлять предупреждения, нам просто нужно найти способ написать код, чтобы он был как правильным, так и свободным от предупреждений.

Путь вперед, на мой взгляд, состоит в том, чтобы признать, что исключения являются неправильным инструментом для использования здесь. Это нормальное поведение для этого преобразования. Вы должны написать код с помощью функций преобразования, которые не вызывают исключения, когда они терпят неудачу. Например, вы можете использовать один из следующих вариантов:

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

Или: [? Почему компилятор предупреждает, что переменная не может быть инициализирована]

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

Компилятор Delphi XE не генерирует переменную ложного предупреждения. Возможно, она не была инициализирована в этом коде. – kludg

-1

Это правильное поведение. Значение строки 1 никогда не будет использоваться, так как в try/except вы назначаете новое значение без использования предыдущего, следовательно, это предупреждение.

если вы удалите строку вы пытаетесь использовать переменные lTime вне Try/за исключением блока, который может потерпеть неудачу, фактически установив значение 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; 
+0

Спасибо, я не знал, что был также StrToInt ** 64 ** Def –

+0

Я не согласен с параграфом 2. На самом деле нет никаких обстоятельств, когда строка 1 удаляется, где 'lTime' можно прочитать до того, как она была инициализирована. –

+0

Предупреждение о линии 2 появляется только при удалении строки 1. Почему это неправильно? –