2011-08-23 35 views
5

首先,對於冗長的代碼示例感到抱歉,但我相信需要說明我的問題。爲什麼一些屬性在監視列表中超出範圍,而其他屬性卻不在?

作爲一個調試幫助,我經常在我的對象上引入一個「DebugString」 - 方法,它返回一個簡潔的對象摘要。但有時我的對象太複雜,無法在單個字符串中最佳地表示,因此我使用了字符串列表。現在,我想用Delphi中出色的調試可視化工具來監視我的對象。我這樣做的方式是引入一個屬性,其中包含重建字符串列表的getter。

這種有點奏效,但對於我追蹤的每一行,該屬性都超出了範圍,所以我必須再次單擊觀察窗口中的放大鏡才能看到該值。爲什麼是這樣?

複製,創建一個新的控制檯應用程序:

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: 儘管馬裏安Venema好好努力,我仍然沒有辦法解決這個問題。我已經向Embarcadero提交了一份報告(質檢編號98062,請投票:-))。但是,我懷疑Embarcadero解決這個問題需要一些時間,並且看到我仍然對解決方案感興趣,我會提供一個小小的獎勵。從來沒有嘗試過,所以這將是有趣的se會發生什麼:-)

+1

什麼版本的Delphi您使用的是在這裏嗎? –

+0

我在XE(確切版本是15.0.3890.34076) –

回答

4

它超出了範圍,因爲這正是執行擾頻時發生的。該錯誤可能是因爲可視化器在回到範圍時不會刷新。還沒有看過TStrings可視化器,但是一個解決方法是使用一個非類型化的指針變量到FDebugStringList,並將該TStringList的一個類型轉換爲該去引用的指針。

var 
    vMyObj : TMyClass; 
    vSL: Pointer; 

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

和手錶:

TStringList(vSL^) 

當你現在開始第二雜亂突破,打開VSL形象化,你會看到FDebugStringList的內容。當你跨過第二次爭奪時,你可以看到可視化器「在爭搶執行時思考,然後在你回到主級時自我刷新」。

陷阱:您確實需要確保untyped指針變量沒有被優化掉。所以要麼做一些不平凡的使用,確保在調試時關閉優化。

編輯:不幸的是,似乎周圍的工作顯示過時的值。見Svein的評論。

更新

免責聲明:我不是ToolsAPI專家。對StringListVisualizer和ToolsAPI單元的粗略檢查表明RefreshVisualizer是「{在可視化器的數據需要刷新時調用}」。此外,字符串「RefreshVisualizer」僅在ToolsAPI單元的接口聲明中找到,並在StringListVisualizer單元中聲明和實現。所以我現在的猜測是調試器應該在第三次Scramble調用中停止時調用RefreshVisualizer,但不調用RefreshVisualizer。我認爲值得質檢報告。至少這是一個「用戶體驗的缺點」。

+0

感謝您的回答。不幸的是,解決方法不能100%工作。顯示器中顯示的值已過時。如果你看都DebugStringList和DebugString,你會看到這些值是不同的,當你跟蹤在接下來的拼字遊戲,從DebugString值顯示出來的DebugStringList,而DebugString顯示的*真正*值。 –

+0

@Svein:呵呵,沒注意到。我想你被卡住了。如果您向Embarcadero提交質檢報告並讓我知道它的編號,我會投票贊成。 –

+0

謝謝你的嘗試。但看起來你是對的。我卡住了。我已經向Embarcadero提交了一份報告。質量控制編號爲98062. –

1

也許Visualiser的不stackframes處理得很好關閉,短吸氣功能stackframeless?

看看是否關閉優化,並stackframes的幫助,然後將結果添加到您的QC。

+0

沒有變化。優化已經結束,關閉堆棧框沒有幫助。 –

+0

stackframes應該打開。但是,確定,它被測試:-) –

1

Are Delphi strings immutable?,德爾福字符串寫入時複製 - 因此,當你改變FDebugStringListRebuildDebugStringlist這大概扔掉舊的字符串的程序內,使其走出去的範圍。

你可以嘗試直接修改字符串(使用指針運算等)來重複使用相同的副本,看看是否能工程?當然,這假定您事先知道調試輸出的最大長度,並可以相應地設置初始字符串長度。

+0

我不知道這是否會起作用,但即使它做到了也不會是最佳解決方案。我想使用可視化工具來觀看_stringlist_,而不是字符串。 –

相關問題