2011-08-23 12 views
5

Antes que nada, disculpe el largo ejemplo del código, pero creo que es necesario para ilustrar mi problema.¿Por qué algunas propiedades quedan fuera del alcance en la lista de observación, mientras que otras no?

Como una herramienta de depuración a menudo introduzco un método "DebugString" en mis objetos, que devuelve un resumen de objeto conciso. Pero a veces mis objetos son demasiado complejos para representarlos de manera óptima en una sola cadena, así que uso listas de cadenas. Ahora, me gustaría usar los excelentes visualizadores de depuración en Delphi para monitorear mi objeto. La forma en que hago esto es introducir una propiedad con un getter que reconstruye la lista de cadenas.

Esto funciona, pero por cada línea que trace, la propiedad se sale del alcance, así que tengo que hacer clic en la lupa en la ventana del reloj nuevamente para ver el valor. ¿Por qué es esto?

Para reproducir, crear una nueva aplicación de consola:

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. Añadir relojes para "vMyObj.DebugStringList" y "vMyObj.DebugString"
  2. Coloque un punto de interrupción en la línea 77 (el segundo "vMyObj .Scramble ") y corre.
  3. Haga clic en la lupa que aparece junto al reloj "DebugStringList" para obtener el visualizador
  4. observar que el visualizador funciona muy bien :)
  5. paso sobre la línea siguiente. El visualizador ahora indica que el reloj está fuera del alcance.
  6. Presione la lupa de nuevo para ver el nuevo estado del objeto.

¿Por qué el visualizador dice que el reloj está fuera del alcance? ¿Cómo puedo arreglar esto?

PD: Sé que puedo escribir visualizadores de depuración, pero utilizo "DebugString" y "DebugStringList" en algunas pruebas automáticas, y realmente me gustaría utilizarlos de esta manera fácil.

Actualización: Me utilizar Delphi XE

Actualización 2: A pesar de un buen esfuerzo por Marjan Venema, todavía tengo ninguna solución a este problema. He presentado un informe con Embarcadero (QC número 98062, vote por favor :-)). Sin embargo, sospecho que tomará un tiempo para que Embarcadero solucione este problema, y ​​viendo que todavía estoy interesado en una solución alternativa, ofreceré una pequeña recompensa. Nunca lo había intentado antes, así que será interesante ver qué sucede :-)

+1

¿Qué versión de Delphi está utilizando aquí? –

+0

Estoy en XE (versión 15.0.3890.34076 para ser exactos) –

Respuesta

4

Sale fuera de alcance porque eso es exactamente lo que sucede cuando se ejecuta Scramble. El error puede deberse a que el visualizador no se actualiza cuando vuelve al alcance. Todavía no he echado un vistazo al visualizador de TStrings, pero una solución alternativa es usar una variable de puntero sin tipo en FDebugStringList y vigilar una conversión de ese TStringList a ese puntero desenredado.

var 
    vMyObj : TMyClass; 
    vSL: Pointer; 

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

y el reloj en:

TStringList(vSL^) 

Cuando se rompe ahora en la segunda pelea, abre el visualizador de VSL, podrás ver el contenido de FDebugStringList. Cuando pisas esa segunda lucha, puedes ver el visualizador "pensando mientras se ejecuta la aleatorización y luego refrescándose a medida que vuelves al nivel principal".

Pitfall: debe asegurarse de que la variable del puntero sin tipo no se optimice. Por lo tanto, haga un uso no trivial para asegurarse de que la optimización esté desactivada durante la depuración.

Edit: Desafortunadamente, parece que el trabajo alrededor muestra valores obsoletos. Ver el comentario de Svein.

actualización

Negación: No soy experto ToolsAPI. Un examen superficial de StringListVisualizer y las unidades ToolsAPI muestra que RefreshVisualizer es "{Llamado cuando los datos para el visualizador necesitan actualizarse}". Además, la cadena "RefreshVisualizer" solo se encuentra en la declaración de interfaz en la unidad ToolsAPI y se declara e implementa en la unidad StringListVisualizer. Así que mi suposición en este momento es que el depurador debería llamar a RefreshVisualizer cuando vuelve para detenerse en la tercera llamada Scramble, pero no lo hace. Vale la pena un informe de control de calidad en mi opinión. Por lo menos, es una "Deficiencia en la experiencia del usuario".

+0

Gracias por su respuesta. Lamentablemente, la solución no funciona al 100%. Los valores que aparecen en el visualizador están desactualizados. Si mira tanto DebugStringList como DebugString, verá que los valores son diferentes, y cuando rastrea sobre el próximo scrabble, los valores de DebugString aparecen en DebugStringList, mientras que los valores * reales * se muestran en DebugString. –

+0

@Svein: Oh, maldito, no me di cuenta. Supongo que estás atascado entonces. Si presenta un informe de control de calidad con Embarcadero y me hace saber su número, lo votaré. –

+0

Gracias por intentarlo. Pero parece que tienes razón; Estoy atascado. He presentado un informe con Embarcadero. El número de control de calidad es 98062. –

1

¿Quizás el visualizador no funciona muy bien con las estructuras de pila apagadas, y las funciones de captador corto son sin marco?

Vea si desactivar la optimización, y apilar marcos ayuda, y agregue el resultado a su control de calidad.

+0

Sin cambios. La optimización ya estaba desactivada, y apagar las estructuras de pila no ayudaba. –

+0

stackframes debería estar activado. Pero está bien, está probado :-) –

1

Según Are Delphi strings immutable?, cadenas de Delphi son copia en escritura - por lo que cuando se cambia FDebugStringList dentro del procedimiento RebuildDebugStringlist es probable que esté desperdiciando la vieja cadena, provocando que vaya fuera de alcance.

¿Puedes intentar modificar la cadena directamente (usando la aritmética del puntero, etc.) para reutilizar la misma copia y ver si funciona? Por supuesto, esto supone que conoce la longitud máxima de la salida de depuración por adelantado y puede establecer la longitud de cadena inicial en consecuencia.

+0

No sé si esto funcionaría, pero incluso si lo hiciera, no sería la solución óptima. Quiero usar los visualizadores para mirar una _stringlist_, no una cadena. –

Cuestiones relacionadas