2017-01-26 82 views
3

我有記錄類型:如何初始化VAR Record參數

type 
    TIPInfo = record 
    IP, 
    HostName, 
    City, 
    Region, 
    Country, 
    Loc, 
    Org: WideString 
    end; 

函數返回的記錄數據:

function GetPublicIPInfo(var IPInfo: TIPInfo): Boolean; 
begin 
    // initialize 
    FillChar(IPInfo, SizeOf(TIPInfo), 0); 

    // populate data 
    IPInfo.IP := GetVallue('ip'); 
    IPInfo.HostName := GetVallue('hostname'); 
    IPInfo.City := GetVallue('city'); 
    // etc... 

    Result := IsOk; 
end; 

來電者:

var 
    IPInfo: TIPInfo; 

if GetPublicIPInfo(IPInfo) then... // use data 

它是正確的通過調用FillChar來初始化var TIPInfo的方法,還是應該將每個字段設置爲空字符串?呼叫者應該這樣做嗎?

另外,在這種情況下使用out參數會更好嗎(因爲函數沒有讀取數據)?

+0

我會用out參數時,函數不讀取參數。此外,在這種情況下,很明顯函數本身應該初始化它而不是調用者。 FillChar沒有在每種情況下正確初始化字符串等託管類型(儘管它在您的情況下),所以我只是分別初始化每個字段。 –

+2

在這種情況下,所有記錄字段都是'WideString',它由編譯器自動初始化,所以函數根本不需要初始化任何東西。這就是說,我同意R.Beiboer,使用'out'而不是'var'是最好的選擇。 –

+0

@RemyLebeau成員可以使用該對象以前使用的任意值。所以他們確實需要分配。 –

回答

5

使用只是FillChar在這裏是錯誤的。如果任何WideString成員不爲空,那麼您將以這種方式泄漏它們。相反,我建議如下:

Finalize(IPInfo); 
FillChar(IPInfo, SizeOf(TIPInfo), 0); 

或者另一種方式是定義默認記錄作爲一個類型的常數:

const 
    DefaultIPInfo: TIPInfo =(); 

然後你可以使用簡單的賦值:

IPInfo := DefaultIPInfo; 

在現代德爾福的版本,你可以使用這個更可讀的代碼:

IPInfo := Default(TIPInfo); 

欲瞭解更多關於這個問題,請參閱以下主題:

注意,在你的代碼泄漏是很難找到,因爲WideString變量作爲COM實現BSTR對象,並在COM堆上分配。因此,如果你使用Delphi內存管理器的內存泄漏檢測功能,泄漏將不會被檢測到,因爲它是從不同的堆泄漏出來的。

對於您的情況,由於您的記錄是託管類型,並且只包含託管類型,因此您可以使用out參數以取得良好效果。對於託管類型,out參數意味着,編譯器將生成的代碼,在調用點,默認傳遞之前初始化記錄

考慮下面的程序:

{$APPTYPE CONSOLE} 

type 
    TRec = record 
    Value: WideString; 
    end; 

procedure Foo1(var rec: TRec); 
begin 
end; 

procedure Foo2(out rec: TRec); 
begin 
end; 

procedure Main; 
var 
    rec: TRec; 
begin 
    rec.Value := 'Foo'; 
    Foo1(rec); 
    Writeln(rec.Value); 
    Foo2(rec); 
    Writeln(rec.Value); 
end; 

begin 
    Main; 
end. 

輸出是:

 
Foo 

如果您的記錄包含託管和非託管類型的組合,那麼情況就不太好。

{$APPTYPE CONSOLE} 

type 
    TRec = record 
    Value1: WideString; 
    Value2: Integer; 
    end; 

procedure Foo1(var rec: TRec); 
begin 
end; 

procedure Foo2(out rec: TRec); 
begin 
end; 

procedure Main; 
var 
    rec: TRec; 
begin 
    rec.Value1 := 'Foo'; 
    rec.Value2 := 42; 
    Foo1(rec); 
    Writeln(rec.Value1); 
    Writeln(rec.Value2); 
    Foo2(rec); 
    Writeln(rec.Value1); 
    Writeln(rec.Value2); 
end; 

begin 
    Main; 
end. 

輸出是:

 
Foo 
42 

42 

只有管理成員是默認初始化參數out。所以最好的選擇是默認初始化變量,即使它作爲out參數傳遞。

更多out參數可以在這裏找到:What's the difference between "var" and "out" parameters?

+0

所以你建議**總是**調用'Finalize(IPInfo); FillChar(IPInfo,SizeOf(TIPInfo),0);'? – zig

+0

我的意見是,你應該編寫代碼,以便它對未來的變化很有用。這意味着默認初始化記錄始終,即使它恰好只包含託管類型。如果你在問題中記錄,那麼你可以使用'out'參數,並依靠編譯器在調用站點默認初始化。但是,在未來的某一天,您將向記錄中添加一個非託管成員,並且幾英里之外的某些功能將會中斷。我的意見是'out'幾乎是無用的。 –

+1

我再次問:我總是可以調用'Finalize(IPInfo); FillChar(IPInfo,SizeOf(TIPInfo),0);'作爲一般**解決方案?有沒有一個通用的解決方案? – zig