Come sono sicuro che tutti sanno, il modo standard di gestire un oggetto è come questo:
A := TMyObject.Create;
try
A.DoSomething;
finally
A.Free;
end;
Se c'è un'eccezione nella TMyObject.Create
allora il distruttore sarà chiamato e quindi l'eccezione sollevata. In tal caso, non sarà assegnato a A
.
Quando si dispone di più oggetti è possibile ripetere il modello:
A := TMyObject.Create;
try
B := TMyObject.Create;
try
A.DoSomething;
B.DoSomething;
finally
B.Free;
end;
finally
A.Free;
end;
Questo diventa molto rapidamente un pasticcio e quindi la domanda.
Un trucco standard è quello di sfruttare il fatto che Free
può essere tranquillamente chiamato su un riferimento all'oggetto nil
.
A := nil;
B := nil;
try
A := TMyObject.Create;
B := TMyObject.Create;
A.DoSomething;
B.DoSomething;
finally
B.Free;
A.Free;
end;
questo ha la debolezza minore che non è resistente alle eccezioni essendo sollevate B.Free
ma non è irragionevole considerare questo come una condizione di errore che può essere ignorato. I distruttori non dovrebbero sollevare eccezioni. Se lo fanno allora il tuo sistema è probabilmente rotto irrimediabilmente.
Questo schema sopra può diventare un po 'disordinato man mano che vengono aggiunti più oggetti, quindi uso personalmente i seguenti metodi di supporto.
procedure InitialiseNil(var Obj1); overload;
procedure InitialiseNil(var Obj1, Obj2); overload;
procedure InitialiseNil(var Obj1, Obj2, Obj3); overload;
procedure FreeAndNil(var Obj1); overload;
procedure FreeAndNil(var Obj1, Obj2); overload;
procedure FreeAndNil(var Obj1, Obj2, Obj3); overload;
Infatti il mio codice ha versioni con ancora più parametri. Per facilità di manutenzione questo codice viene generato automaticamente da un breve script Python.
Questi metodi sono implementati in modo ovvio, ad es.
procedure FreeAndNil(var Obj1, Obj2);
var
Temp1, Temp2: TObject;
begin
Temp1 := TObject(Obj1);
Temp2 := TObject(Obj2);
Pointer(Obj1) := nil;
Pointer(Obj2) := nil;
Temp1.Free;
Temp2.Free;
end;
Questo ci permette di riscrivere l'esempio precedente come questo:
InitialiseNil(A, B);
try
A := TMyObject.Create;
B := TMyObject.Create;
A.DoSomething;
B.DoSomething;
finally
FreeAndNil(B, A);
end;
stavo postando una risposta molto simile, una piccola modifica sarebbe quella di mettere 'newOrderSource: = TWebNewOrderSource.Create() ; 'prima del tentativo, e butta la' newOrderSource: = nil; '. –
Anche questo funziona e rimuove un compito non necessario, ma ho preferito la semplicità e la coerenza in questo caso perché è altamente improbabile che il salvataggio del compito farebbe molta differenza se "fai roba" chiama in un database. – chuckj
Io lo uso sempre, ma per quanto riguarda la sicurezza, si dovrebbe notare che, rispetto al codice originale di OP, questo protegge da eccezioni sollevate in qualsiasi costruttore ma ** non ** da eccezioni generate durante la liberazione degli oggetti. Se sei paranoico a riguardo, la tua unica opzione è quella di avere tanti try/finally in quanto ci sono oggetti (o averli implementare interfacce e lasciarli andare fuori ambito). –