4

我最近偶然發現了一些我寫的非常舊的代碼造成的問題,顯然假設在with語句中使用的接口引用會在with - 塊留下時立即發佈 - 有點像隱含的try-finally - 塊(類似於C#的using -statement,如果我理解正確)。德爾福:從什麼時候接口引用不再在with-block結束時釋放?

顯然(在Delphi 2009中)這不是(不再是?)的情況。有誰知道這是什麼時候發生的?或者是我的代碼剛開始是錯誤的?

要澄清一下,這裏有一個簡單的例子:

type 
    IMyIntf = interface; 
    TSomeObject = class(TInterfacedObject, IMyIntf) 
    protected 
    constructor Create; override; // creates some sort of context 
    destructor Destroy; override; // cleans up the context created in Create 
    public 
    class function GetMyIntf: IMyIntf; //a factory method, calling the constructor 
    end; 

procedure TestIt; 
begin 
    DoSomething; 
    with (TSomeObject.GetMyIntf) do 
    begin 
     DoStuff; 
     DoMoreStuff; 
    end; // <- expected: TSomeObject gets destroyed because its ref.count is decreased to 0 
    DoSomethingElse; 
end; // <- this is where TSomeObject.Destroy actually gets called 

每當有人開始老「with是邪惡的」的說法,這是永遠的一個例子,我腦子裏想這讓我繼續走下去「是的,但.. 「。似乎我錯了......任何人都可以確認嗎?

回答

17

with Pascal/Delphi中的保留字僅用於輕鬆訪問記錄或對象/類的成員(即爲了不提及記錄/對象/類的名稱)。它與垃圾收集相關的C#with完全不同。自從records出生以來,它就以Pascal語言存在,以簡化對許多數據成員(當時簡稱爲「字段」)的代碼調用。

總而言之,with與垃圾收集,釋放內存或破壞對象實例無關。在with頭部構造的對象可以在之前單獨的代碼行中初始化,它們是相同的。

+3

正確的,對象的創建範圍是它的主人,在程序這個案例。在上面的例子中,它應該在最後結束時被銷燬。任何其他行爲都會出乎意料,並且可能是D2009中糾正的錯誤。 – dverespey 2009-08-05 15:12:54

+4

自從在Delphi 3中引入接口以來,行爲一直是一樣的。沒有任何錯誤被糾正。奧利弗只是錯誤地記得事情是如何運作的。又一個「帶」的例子導致意想不到的結果。 – 2009-08-05 17:21:09

+1

我總是想象用''來創建一個匿名的,範圍很大的局部變量,它只存在於''with''塊中。對我來說,對於內存管理員來說,即使當你不能再訪問它時,對我來說似乎毫無意義... 鑑於(現在看起來:可悲地不正確)世界觀我認爲它完全有效期望引用計數在參考值超出範圍時立即遞減...... – 2009-08-06 11:25:49

3

此WITH行爲從未改變。爲了達到你的預期行爲,你可以用這種方式更改代碼:

procedure TestIt; 
    var 
     myIntf: IMyIntf; 
    begin 
     DoSomething; 

     myIntf := TSomeObject.GetMyIntf 
     DoStuff; 
     DoMoreStuff; 
     myIntf := nil; // <- here is where TSomeObject.Destroy called 

     DoSomethingElse; 
    end; 

,或者你可以在程序做到這一點:

procedure TestIt; 

    procedure DoAllStuff; 
    var 
     myIntf: IMyIntf; 
    begin 
     myIntf := TSomeObject.GetMyIntf 
     DoStuff; 
     DoMoreStuff; 
    end; // <- here is where TSomeObject.Destroy called 

begin  
    DoSomething; 
    DoAllStuff; 
    DoSomethingElse; 
end; 
+0

不幸的是,兩種選擇都違背了保持代碼簡潔的主要目的之一......在第一個例子中,爲了清晰起見,我會添加一個try-finally塊 我也不喜歡聲明變量「空的」接口類型完全是爲了濫用^ D^D^D^D^Dtake接口對象的引用計數性質的好處...我喜歡在可能的情況下保留那些匿名的...但是這無疑只是一個個人我的特質 – 2009-08-06 11:15:24