PDA

Visualizza la versione completa : Discriminare chiamate a metodi e a procedure


mondobimbi
22-07-2006, 15:42
ciao a tutti,
ho un classe derivata da TStringList


type mia_classe = class (TStringList)
...



procedure mia_classe.Free
var
FileLog : TextFile;
begin

// prima di liberare la memoria faccio un log
AssignFile(FileLog, 'log/mio_log.log');

// vorrei appendere i miei dati ma il compilatore cerca di richiamare
// il metodo membro di TStringList Append
Append(FileLog);

WriteLn(FileLog, thr_web_msg);
CloseFile (FileLog);

// ok vai al metodo del genitore
inherited;

end;

come ho messo nel commento il compilatore mi dà errore quando cerco di chiamare il metodo Append per appendere i dati al file aperto FileLog, mentre logicamente ReWrite(FileLog) funziona correttamente.
Come faccio a chiamare la funzione Append??

grazie a tutti
sergio

alka
22-07-2006, 16:00
Originariamente inviato da mondobimbi
come ho messo nel commento il compilatore mi dà errore quando cerco di chiamare il metodo Append per appendere i dati al file aperto FileLog, mentre logicamente ReWrite(FileLog) funziona correttamente.
Come faccio a chiamare la funzione Append??

Fai precedere al nome della procedura Append la unit in cui è definita questa funzione, cioè System.



System.Append(FileLog);


Ad ogni modo, io non introdurrei simili operazioni all'interno di un metodo che viene richiamato quando un oggetto viene distrutto: si rischia di compromettere la corretta distruzione dello stesso in caso di errore od eccezione.

In secondo luogo, il metodo Free non è virtuale, pertanto non può essere ridefinito nelle classi discendenti: eventuali deinizializzazioni vanno inserite nel metodo Destroy che è il metodo da ridefinire ed è preposto a questa funzione.

Non si tratta solo di una convenzione, ma vi sono anche motivi pratici per cui si segue questa strada.

Inoltre, non puoi garantire che la tua implementazione venga richiamata, proprio perché il metodo Free non è virtuale e non può essere ridefinito (al contrario del metodo Destroy): se un oggetto appartenente alla classe che hai creato viene assegnata ad una variabile di tipo TStringList o appartenente a qualsiasi classe da cui discende la tua, chiamando il metodo Free attraverso quel riferimento non viene eseguito il tuo codice, bensì quello della classe base in cui viene introdotto tale metodo.

Insomma, al di là del problema riscontrato, la soluzione che hai adottato risulta "fumosa" sotto molti punti di vista secondo i canoni convenzionali e funzionali della programmazione Delphi.

Ciao! :ciauz:

mondobimbi
22-07-2006, 17:54
grazie dell'indicazione.

Ci ho lavorato un po' per trovare quale fosse la strada giusta per assicurarmi che tutto fosse rilasciato nella maniera corretta, il meglio che ho pensato è di inserire tutto nel metodo Free, anche perchè, per abitudine, così libero gli oggetti.
Ti posto il codice della procedura Free della classe TTBase deriva da TXML che deriva da TStringList.
Implementa la creazione di una pagina xml che viene appunto scritta su un file testo quando viene chiamata la procedura Free e trasferita in remoto.


procedure TTBase.Free;
var
nome_file : string;
locale_adr, remote_adr : string;
host, user, psw : string;
res : boolean;
begin

// lo preleva dalle impostazioni
locale_adr := SetLocaleAdrMacro(IniFile.ReadString('Indirizzi Fisici', 'Root', ''));

with DominiTbl do
begin

remote_adr := FieldByName('adr_root');
remote_adr := SetRemoteAdrMacro(fDominio, remote_adr);

host := FieldByName('ftp_host');
user := FieldByName('ftp_user');
psw := FieldByName('ftp_password');

end;


nome_file := GetFileName;
SaveToFile(locale_adr + '/' + nome_file);

res := FtpPutFile(host, '21', remote_adr + '/' + nome_file, locale_adr + '/' + nome_file, User, psw);

with TLogFile.Create ('gesti.log') do
begin

if not res then
Write('non è stato possibile trasferire il file ' + nome_file)
else
Write('file ' + nome_file + ' correttamente trasferito via ftp');

Free;

end;

with DominiTbl do Free;

inherited;

end;

in questa modo sono sicuro che quando libero la memoria di un oggetto derivato il tutto mi viene scritto su disco e trasferito in remoto semplicamente scrivento oggett.Free.

Una classe figlia che implementa un layout a tre colonne ha il metodo Free


procedure TLAY3.Free;
begin

Current := 'inside_sx';
AddChild('div');
Attributo('class', 'clear');
AddContent('');

Current := 'inside_dx';
AddChild('div');
Attributo('class', 'clear');
AddContent('');

Current := 'content_tag';
AddChild('div');
Attributo('class', 'clear');
AddContent('');

Current := 'innerColumnContainer';
AddChild('div');
Attributo('class', 'clear');
AddContent('');


inherited;

end;

l'inherited finale mi assicura che tutta la catena venga eseguita prima della distruzione dell'oggetto.

Se ti sembra che sia poco sicuro potrei provare a spostare il codice in Destroy.
ciao e grazie
sergio

alka
22-07-2006, 18:05
Originariamente inviato da mondobimbi
grazie dell'indicazione.

Non so a cosa sia dovuto il ringraziamento... nel tuo codice, non c'è una sola cosa che rispecchi quanto ti ho detto nel messaggio precedente. :)

mondobimbi
22-07-2006, 18:15
era per il System.Append
ciao

alka
22-07-2006, 18:22
Originariamente inviato da mondobimbi
era per il System.Append

Non ti seguo più... :stordita:

Mi riferivo a quanto ho scritto in merito a

se possibile, non si introduce "business logic" in distruzione, altrimenti si rischia di non poter distruggere l'oggetto a causa di eccezioni;
non si implementa la distruzione nel metodo Free, ma in Destroy, in caso contrario si rischia che il metodo Free non venga chiamato (l'override di Destroy è possibile proprio per questo motivo).


Introdurre finalizzazioni all'interno del metodo Free, tenuto conto dei meccanismi di funzionamento della libreria VCL e del linguaggio, è errato.

mondobimbi
22-07-2006, 19:51
scusami ma mi ci devo pensare un pochino.

In realtà era mia intenzione inserire nel metodo Free non la distruzione degli oggetti (che concettualmente, hai ragione, vanno nel metodo Destroy) ma un insieme di operazioni comuni a tutti gli oggetti derivati: in questo caso inserimento di tag conclusivi in pagine xhtml, scrittura del file su disco ed eventualmente trasferimento in remoto via ftp. La catena delle chiamate a Free si sussegue ordinatamente come da me pensato dall'ultimo figlio a scendere fino all'antenato più lontano, questo lo ho verificato con il debugger. Mai la catena si è interrotta e la chiamata è stata fatta direttamente ad un genitore.

Per quanto riguarda la gestione delle eccezioni sebbene io non lo abbia fatto è possibile implementarlo tranquillamente in Free e chiuderlo nel blocco con un finally inherited; che passa la palla al Free del genitore e a crescere in una catena di Free e Destroy.

ciao
sergio

alka
22-07-2006, 19:58
Originariamente inviato da mondobimbi
La catena delle chiamate a Free si sussegue ordinatamente come da me pensato dall'ultimo figlio a scendere fino all'antenato più lontano, questo lo ho verificato con il debugger. Mai la catena si è interrotta e la chiamata è stata fatta direttamente ad un genitore.

Per forza: non hai verificato il caso specifico che dà luogo a questo effetto collaterale.

Prova questo codice:



var
Obj: TObject;

Obj := TTBase.Create;

// ...

Obj.Free;


Vedrai che la chiamata al metodo Free non esegue il tuo codice, poiché il metodo Free non va modificato e non va reintrodotto, ma è necessario fare l'override del metodo Destroy.

A parte tutto, io non capisco quale sia il problema nello spostare il codice da Free a Destroy... certo, a meno che tu non voglia ritrovarti con effetti collaterali e problemi in più rispetto a quelli che dovrebbe normalmente risolvere il software. :master:

mondobimbi
23-07-2006, 11:32
questa è la procedura che utilizzo per la generazione di una pagina web.
I dati relativi alla pagina sono nel database (mysql, TmyQuery è una classe che agisce direttamente sulle API), la struttura è invece nelle classi.
Il costruttore organizza la struttura, la procedura Free inserisce codici di finalizzazione.
Le procedure Free sono chiamate a scendere attraverso la chiamata ad inherited fino al progenitore che è TStringList.
La chiamata a Free di TStringList, come classe vcl, chiama alla fine della catena Destroy, che essendo virtuale è il Destroy dell'oggetto creato (nel mio caso della classe TLAY3_ECOM) che non ho bisogno di chiamare direttamente appunto perchè virtuale, e poi a scendere disciplinatamente attraverso le classi genitrici.


procedure genera_pagina_new (dominio, pagina : string);
var
tipo : string;
begin

with TmyQuery.Create do
begin

SQL.Add('select tipo from PagineSito');
SQL.Add('where dominio="' + dominio + '"');
SQL.Add('and pagina="' + pagina + '"');

Open;

// tipo pagina web: html, php
if not (Eof and Bof) then
tipo := FieldByName('tipo')
else tipo := 'html';

Free;

end;

with TLAY3_ECOM.Create (dominio, pagina, '/' + pagina + '.' + tipo) do Free;

end;

Il tutto mi funzione perfettamente con l'accortezza sempre di inserire inherited alla fine di ciascuna procedura Free, altrimenti la catena si interrompe.

Una osservazione, questo codice è Free Pascal e lo utilizzo su Lazarus, non so se il comportamente in Delphi possa essere differente, ma non penso.

Il fatto che ho utilizzato la procedura Free (che comunque nella classe base vcl chiama Destroy) e perchè ho qui inserito del codice di finalizzazione, mentre il codice relativo alla distruzione di eventuali oggetti creati all'interno della classe ho cercato di inserirlo in Destroy.

ti ringrazio della opportunità che hai colto di chiarimento di un argomento di logica base che non è sempre facile aver l'ocasione di poter discutere

ciao
sergio

alka
23-07-2006, 15:04
Originariamente inviato da mondobimbi
Il fatto che ho utilizzato la procedura Free (che comunque nella classe base vcl chiama Destroy) e perchè ho qui inserito del codice di finalizzazione, mentre il codice relativo alla distruzione di eventuali oggetti creati all'interno della classe ho cercato di inserirlo in Destroy.

Penso che ripetere per l'ennesima volta che *NON* si deve "reintrodurre" il metodo Free ma al contrario ridefinire il metodo Destroy poiché virtuale e richiamato qualunque sia il tipo della variabile che contiene il riferimento all'oggetto da distruggere potrebbe essere colto come una presa in giro, visto che l'ho scritto già mille volte. Quindi, diciamo che ci rinuncio. :)


Originariamente inviato da mondobimbi
ti ringrazio della opportunità che hai colto di chiarimento di un argomento di logica base che non è sempre facile aver l'ocasione di poter discutere

Non è per essere presuntuoso, ma non credo che la discussione e i chiarimenti che ho portato abbiano trovato un tuo grande interesse: sin dalla prima risposta, ho cercato di darti vari suggerimenti motivandoli per altro in modo tecnico, portando esempi, ma sono stati ogni volta ignorati, come se avessi confermato che la strada che hai scelto fosse corretta.

Non capisco quindi, onestamente, da dove arrivi il tuo ringraziamento per aver discusso di una cosa che, dopo tanti messaggi, nella tua soluzione finale hai ignorato, come se nessuno avesse mai detto il contrario. E' una logica che mi risulta alquanto oscura, ma contento tu... :stordita:

Loading