2011-08-23 10 views
5

Prima di tutto, mi dispiace per il lungo esempio di codice, ma credo che sia necessario per illustrare il mio problema.Perché alcune proprietà escono dall'ambito dell'elenco di controllo, mentre altre no?

Come aiuto per il debug, spesso introduco un metodo "DebugString" sui miei oggetti, che restituisce un sommario conciso dell'oggetto. Ma a volte i miei oggetti sono troppo complessi per essere rappresentati in modo ottimale in una singola stringa, quindi uso stringlist. Ora, vorrei usare gli eccellenti visualizzatori di debug in Delphi per monitorare il mio oggetto. Il modo in cui lo faccio è di introdurre una proprietà con un getter che ricostruisce la lista di stringhe.

Questo tipo funziona, ma per ogni linea di cui faccio traccia, la proprietà diventa fuori portata, quindi devo fare di nuovo clic sulla lente di ingrandimento nella finestra di controllo per visualizzare il valore. Perchè è questo?

Per riprodurre, creare una nuova applicazione console:

program Project1; 

{$APPTYPE CONSOLE} 

uses 
    SysUtils, 
    Classes; 

type 
    TMyClass = class 
    private 
    FInternalData : array[0..4] of integer; 
    FDebugStringList : TStringList; 
    procedure RebuildDebugStringlist; 
    function GetDebugStringList: TStringList; 
    function GetDebugString : string; 
    public 
    constructor Create; 
    destructor Destroy; override; 
    procedure Scramble; 
    property DebugStringList : TStringList read GetDebugStringList; 
    property DebugString : string read GetDebugString; 
    end; 

constructor TMyClass.Create; 
begin 
    FDebugStringList := TStringList.Create; 
end; 

destructor TMyClass.Destroy; 
begin 
    FDebugStringList.Free; 
    inherited; 
end; 

function TMyClass.GetDebugString: string; 
var 
    I : integer; 
begin 
    Result := 'Object state: '; 
    for I := 0 to 3 do 
    Result := Result + inttostr(FInternalData[I])+' '; 
end; 

function TMyClass.GetDebugStringList: TStringList; 
begin 
    RebuildDebugStringlist; 
    Result := FDebugStringlist; 
end; 

procedure TMyClass.RebuildDebugStringlist; 
var 
    I : integer; 
begin 
    FDebugStringList.Clear; 

    FDebugStringList.Add('Object state:'); 
    for I := 0 to 4 do 
    FDebugStringList.Add(inttostr(FInternalData[I])); 
end; 

procedure TMyClass.Scramble; 
var 
    I : integer; 
begin 
    for I := 0 to 4 do 
    FInternalData[I] := Random(100); 
end; 

var 
    vMyObj : TMyClass; 

begin 
    vMyObj := TMyClass.Create; 
    try 
    vMyObj.Scramble; 
    vMyObj.Scramble; 
    vMyObj.Scramble; 
    finally 
    vMyObj.Free; 
    end; 

    readln; 
end. 
  1. Aggiungi orologi per "vMyObj.DebugStringList" e "vMyObj.DebugString"
  2. Posizionare un punto di interruzione sulla linea 77 (il 2 ° "vMyObj .Scramble "), ed eseguire.
  3. Fare clic sulla lente d'ingrandimento accanto all'orologio "DebugStringList" per ottenere il visualizzatore
  4. osservano che il visualizzatore funziona bene :)
  5. Passo sopra la riga successiva. Il visualizzatore ora indica che l'orologio è fuori portata.
  6. Premere nuovamente la lente di ingrandimento per visualizzare il nuovo stato dell'oggetto.

Perché il visualizzatore dice che l'orologio è fuori portata? Come posso risolvere questo?

PS: So che posso scrivere visualizzatori di debug, ma uso "DebugString" e "DebugStringList" in alcuni test automatici, e mi piacerebbe davvero usarli in questo modo facile.

Aggiornamento: Io uso Delphi XE

Aggiornamento 2: Nonostante un buon sforzo da Marjan Venema, ho ancora alcuna soluzione a questo problema. Ho presentato un rapporto con Embarcadero (numero QC 98062, per favore, vota :-)). Tuttavia, sospetto che ci vorrà del tempo perché Embarcadero risolva questo problema, e vedendo come sono ancora interessato a una soluzione alternativa, offrirò una piccola taglia. Non l'ho mai provato prima, quindi sarà interessante vedere cosa succede :-)

+1

Quale versione di Delphi stai usando qui? –

+0

Sono su XE (versione 15.0.3890.34076 per l'esattezza) –

risposta

4

Esce dallo scope perché è esattamente ciò che accade quando Scramble viene eseguito. L'errore potrebbe essere che il visualizzatore non viene aggiornato quando torna in ambito. Non ho ancora dato un'occhiata al visualizzatore di TStrings, ma una soluzione consiste nell'usare una variabile puntatore non tipizzata in FDebugStringList e mettere un orologio su un typecast di quella TStringList su quel puntatore derefenced.

var 
    vMyObj : TMyClass; 
    vSL: Pointer; 

{$OPTIMIZATION OFF} 
begin 
    vMyObj := TMyClass.Create; 
    try 
    vSL := @(vMyObj.FDebugStringList); 

e l'orologio su:

TStringList(vSL^) 

Quando ora si interrompe al secondo scramble, apre il visualizzatore per VSL, vedrete il contenuto del FDebugStringList. Quando passi su quel secondo scramble, puoi vedere il visualizzatore "pensare mentre scramble si esegue e poi si aggiorna tornando al livello principale".

Pitfall: è necessario assicurarsi che la variabile del puntatore non tipizzata non venga ottimizzata. Quindi, fai un uso non banale di questo assicurandoti che l'ottimizzazione sia disattivata durante il debug.

Modifica: Purtroppo sembra che il lavoro svolto mostri valori obsoleti. Vedi il commento di Svein.

Aggiornamento

di responsabilità: non sono un esperto ToolsAPI. Un rapido esame di StringListVisualizer e delle unità ToolsAPI mostra che RefreshVisualizer è "{Chiamato quando i dati per il visualizzatore devono essere aggiornati}". Inoltre, la stringa "RefreshVisualizer" si trova solo nella dichiarazione dell'interfaccia nell'unità ToolsAPI e dichiarata e implementata nell'unità StringListVisualizer. Quindi la mia ipotesi al momento è che il debugger dovrebbe chiamare RefreshVisualizer quando torna a fermarsi sulla terza chiamata Scramble, ma non lo fa. Vale la pena un rapporto di controllo di qualità a mio parere. Per lo meno si tratta di una "User experience shortcoming".

+0

Grazie per la risposta. Sfortunatamente la soluzione alternativa non funziona al 100%. I valori visualizzati nel visualizzatore sono obsoleti. Se guardi sia DebugStringList che DebugString, vedrai che i valori sono diversi e quando esegui il trace sul prossimo scrabble, i valori di DebugString appaiono in DebugStringList, mentre i valori * real * sono mostrati in DebugString. –

+0

@Svein: Oh bugger, non l'ho notato. Immagino che tu sia bloccato allora. Se invii un rapporto di controllo qualità con Embarcadero e fammi sapere il suo numero, voterò a favore. –

+0

Grazie per aver provato. Ma sembra che tu abbia ragione; Sono bloccato. Ho presentato un rapporto con Embarcadero. Il numero CQ è 98062. –

1

Forse il visualizzatore non si comporta molto bene con gli stackframes disattivati ​​e le funzioni getter brevi sono senza stack?

Verificare se disattivare l'ottimizzazione e attivare gli stackframes e aggiungere il risultato al QC.

+0

Nessun cambiamento. L'ottimizzazione era già disattivata e disattivare gli stackframes non era d'aiuto. Gli stack stack –

+0

dovrebbero essere accesi. Ma ok, è testato :-) –

1

Secondo Are Delphi strings immutable?, stringhe Delphi sono copy on write - in modo che quando si cambia FDebugStringList all'interno della procedura di RebuildDebugStringlist probabilmente è buttare via il vecchio stringa, facendolo uscire di portata.

Si può provare a modificare direttamente la stringa (utilizzando l'aritmetica del puntatore ecc.) Per riutilizzare la stessa copia e vedere se funziona? Ovviamente questo presuppone che tu conosca la lunghezza massima dell'output di debug in anticipo e possa impostare la lunghezza della stringa iniziale di conseguenza.

+0

Non so se questo potrebbe funzionare, ma anche se lo facesse, non sarebbe la soluzione ottimale. Voglio usare i visualizzatori per guardare una _stringlist_, non una stringa. –