2017-01-26 15 views
3

У меня есть тип записи:Как инициализировать параметр вар 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 

ли это правильно способ инициализировать var TIPInfo, вызвав FillChar, или я должен установить каждое поле в пустую строку? Должен ли вызывающий абонент сделать это?

Кроме того, было бы лучше использовать параметр out в случае (поскольку функция не считывает данные)?

+0

Я хотел бы использовать выходной параметр, если функция не читает параметр. Кроме того, в этом случае ясно, что сама функция должна инициализировать его, а не вызывающего. FillChar неправильно инициализирует управляемые типы, такие как строки в каждой ситуации (хотя это и есть в вашем случае), поэтому я бы просто инициализировал каждое поле отдельно. –

+2

В этом случае все поля записи являются «WideString», которые автоматически инициализируются компилятором, поэтому функции вообще не нужно ничего инициализировать. Это, как говорится, я согласен с Р.Бейбором, использование 'out' вместо' var' - лучший выбор. –

+0

@RemyLebeau Члены могут иметь произвольные значения от предыдущего использования этого объекта. Поэтому их нужно назначить. –

ответ

5

Использование только FillChar здесь неправильное. Если какой-либо из членов WideString не пуст, вы пронесете их таким образом. Вместо этого я предлагаю следующее:

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

Или другой способ, чтобы определить запись по умолчанию, как типизированный константа:

const 
    DefaultIPInfo: TIPInfo =(); 

Затем вы можете использовать простое задание:

IPInfo := DefaultIPInfo; 

В современной версии Delphi вы можете использовать этот гораздо более читаемый код:

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' и полагаться на компилятор на init по умолчанию на сайте вызова. Но затем в один прекрасный день в будущем вы добавите к записи не управляемый член, а какая-то часть миль будет разорвана. Мое мнение таково, что 'out' близок к бесполезности. –

+1

Я снова спрашиваю: могу ли я всегда вызывать 'Finalize (IPInfo); FillChar (IPInfo, SizeOf (TIPInfo), 0); 'как ** общее ** решение? существует ли вообще решение? – zig