5

首先,对于冗长的代码示例感到抱歉,但我相信需要它来说明我的问题。

作为调试帮助,我经常在我的对象上引入一个“DebugString”方法,它返回一个简洁的对象摘要。但有时我的对象过于复杂,无法在单个字符串中以最佳方式表示,因此我使用字符串列表。现在,我想使用 Delphi 中出色的调试可视化工具来监控我的对象。我这样做的方法是引入一个带有重建字符串列表的吸气剂的属性。

这有点工作,但是对于我跟踪的每一行,属性都会超出范围,所以我必须再次单击监视窗口中的放大镜才能查看值。为什么是这样?

要重现,请创建一个新的控制台应用程序:

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. 为“vMyObj.DebugStringList”和“vMyObj.DebugString”添加监视
  2. 在第 77 行(第二个“vMyObj.Scramble”)放置一个断点,然后运行。
  3. 单击“DebugStringList”手表旁边的放大镜以获取可视化工具
  4. 观察可视化器工作得很好:)
  5. 跨过下一行。可视化工具现在指示手表超出范围。
  6. 再次按下放大镜可以看到物体的新状态。

为什么可视化工具说手表超出范围?我怎样才能解决这个问题?

PS:我知道我可以编写调试可视化工具,但我在一些自动测试中使用“DebugString”和“DebugStringList”,我真的很想以这种简单的方式使用它们。

更新:我使用 Delphi XE

更新 2: 尽管 Marjan Venema 做出了很大的努力,但我仍然无法解决这个问题。我已经向 Embarcadero 提交了一份报告(QC 编号 98062,请投票:-))。但是,我怀疑 Embarcadero 需要一些时间来解决这个问题,并且看到我仍然对解决方法感兴趣,我会提供一个小的赏金。以前从未尝试过,所以看看会发生什么会很有趣:-)

4

3 回答 3

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,但没有。在我看来,值得一份 QC 报告。至少这是一个“用户体验缺陷”。

于 2011-08-23T17:02:59.257 回答
1

也许可视化器不能很好地处理关闭的堆栈帧,而简短的 getter 函数是无堆栈的?

查看是否关闭优化和打开堆栈帧有帮助,并将结果添加到您的 QC。

于 2011-08-27T11:48:35.730 回答
1

根据德尔福字符串不可变吗?, Delphi 字符串是在写入时复制的 - 因此当您FDebugStringList在程序内部进行更改时,RebuildDebugStringlist它可能会丢弃旧字符串,导致它超出范围。

您可以尝试直接修改字符串(使用指针算法等)以重用相同的副本,看看是否有效?当然,这假设您事先知道调试输出的最大长度,并且可以相应地设置初始字符串长度。

于 2011-08-30T22:45:52.433 回答