首先,对于冗长的代码示例感到抱歉,但我认为需要说明我的问题。
作为调试帮助,我经常在我的对象上引入一个“DebugString”方法,它返回一个简洁的对象摘要。但有时我的对象太复杂而无法在单个字符串中进行最佳表示,因此我使用字符串列表。现在,我想使用Delphi中出色的调试可视化器来监视我的对象。我这样做的方法是引入一个带有getter的属性来重建stringlist。
这种方法有效,但对于我追踪的每一行,属性都超出了范围,因此我必须再次单击观察窗口中的放大镜才能看到该值。为什么是这样?
要重现,请创建一个新的控制台应用程序:
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.
为什么可视化器会说手表超出范围?我该如何解决这个问题?
PS:我知道我可以编写调试可视化工具,但我在一些自动测试中使用“DebugString”和“DebugStringList”,我真的很想以这种简单的方式使用它们。更新:我使用的是Delphi XE
更新2: 尽管Marjan Venema做出了很好的努力,但我仍然无法解决这个问题。我已经向Embarcadero提交了一份报告(QC编号98062,请投票:-))。但是,我怀疑Embarcadero需要一些时间来解决这个问题,并且看到我仍然对一个变通方法感兴趣,我会提供一笔小额奖金。从来没有尝试过,所以发生的事情会很有趣: - )
答案 0 :(得分:4)
它超出范围,因为这正是执行Scramble时发生的情况。该错误可能是因为可视化器在返回范围时不会刷新。还没有看过TStrings可视化工具,但解决方法是使用无类型指针变量到FDebugStringList,并将监视器放在该TStringList的类型转换为该解除指针的指针上。
var
vMyObj : TMyClass;
vSL: Pointer;
{$OPTIMIZATION OFF}
begin
vMyObj := TMyClass.Create;
try
vSL := @(vMyObj.FDebugStringList);
和手表:
TStringList(vSL^)
当您现在打破第二次争夺时,打开vSL的可视化工具,您将看到FDebugStringList的内容。当您跨过第二次争夺时,您可以看到可视化工具“在争夺执行时思考,然后在您返回主级别时自我刷新”。
陷阱:您确实需要确保无法优化无类型指针变量。因此,要么对其进行一些非平凡的使用,请确保在调试时关闭优化。
编辑:不幸的是,似乎工作周围显示了过时的值。见Svein的评论。
<强>更新强>
免责声明:我不是ToolsAPI专家。对StringListVisualizer和ToolsAPI单元的粗略检查表明,RefreshVisualizer是“{当可视化器的数据需要刷新时调用}”。此外,字符串“RefreshVisualizer”仅在ToolsAPI单元的接口声明中找到,并在StringListVisualizer单元中声明和实现。所以我现在的猜测是调试器应该在第三次Scramble调用时返回停止时调用RefreshVisualizer,但事实并非如此。在我看来值得一个质量控制报告。至少它是“用户体验的缺点”。
答案 1 :(得分:1)
也许视觉效果不能很好地处理关闭的帧架,而短的getter功能是无帧架的?
查看是否关闭优化,并在堆栈框架上启用帮助,并将结果添加到QC。
答案 2 :(得分:1)
根据Are Delphi strings immutable?,Delphi字符串是写入时的副本 - 所以当您在过程FDebugStringList
内更改RebuildDebugStringlist
时,它可能会丢弃旧的字符串,导致它离开范围。
您是否可以尝试直接修改字符串(使用指针算法等)重用相同的副本,看看是否有效?当然,这假设您事先知道调试输出的最大长度,并可以相应地设置初始字符串长度。