2017-01-26 4 views
3

Ich habe den Datensatztyp initialisieren:Wie var Rekord Parameter

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

Funktion zurückzukehren, um die Aufzeichnungsdaten:

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; 

Der Anrufer:

var 
    IPInfo: TIPInfo; 

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

Ist es die richtige Weg, um die var TIPInfo durch den Aufruf FillChar zu initialisieren oder sollte ich jedes Feld auf leere Zeichenfolge setzen? Sollte der Anrufer das tun?

Auch wäre es besser, einen out Parameter in dem Fall zu verwenden (da die Funktion nicht, um die Daten zu lesen)?

+0

ich ein out-Parameter verwenden würde, wenn die Funktion nicht den Parameter liest. Auch in diesem Fall ist es klar, dass die Funktion selbst es initialisieren sollte und nicht der Aufrufer. FillChar initialisiert verwaltete Typen wie Strings nicht in jeder Situation (obwohl dies in Ihrem Fall der Fall ist), also initialisiere ich jedes Feld einzeln. –

+2

In diesem Fall sind alle Datensatzfelder "WideString", das vom Compiler automatisch initialisiert wird, so dass die Funktion überhaupt nichts initialisieren muss. Davon abgesehen stimme ich R. Beiboer zu, die Verwendung von "out" statt "var" ist die beste Wahl. –

+0

@RemyLebeau Die Mitglieder könnten willkürliche Werte von einer früheren Verwendung dieses Objekts haben. Sie müssen also zugewiesen werden. –

Antwort

5

Mit nur FillChar falsch ist hier. Wenn eines der WideString Mitglieder nicht leer ist, werden Sie diese auf diese Weise verlassen. Stattdessen schlage ich folgendes:

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

Oder eine andere Art und Weise ist ein Standardsatz als definieren typisierten Konstante:

const 
    DefaultIPInfo: TIPInfo =(); 

Dann können Sie einfache Zuordnung verwenden:

IPInfo := DefaultIPInfo; 

In der modernen Versionen von Delphi können Sie stattdessen diesen viele lesbaren Code verwenden:

IPInfo := Default(TIPInfo); 

Weitere Informationen zu diesem speziellen Thema finden Sie in folgenden Themen:

Beachten Sie, dass das Leck in Ihrem Code ist schwer zu finden, weil WideString Variablen als COM BSTR Objekte implementiert werden, und auf dem COM-Heap zugewiesen. Wenn Sie Speicherleckerkennung für den Delphi-Speicher-Manager verwenden, wird das Leck daher nicht erkannt, da es von einem anderen Heap ausgelaufen ist.

Da Ihr Datensatz ein verwalteter Typ ist und nur verwaltete Typen enthält, könnten Sie in diesem Fall einen out-Parameter mit gutem Ergebnis verwenden. Für verwaltete Typen, bedeuten out Parameter, dass der Compiler Code, an der Aufrufstelle generieren, auf Standardeintrag initialisieren, bevor es im Vorbeigehen

Betrachten Sie das folgende Programm:.

{$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. 

Die Ausgabe lautet:

Wenn Ihr Datensatz eine Mischung aus verwalteten und nicht verwalteten Typen enthält, ist die Situation nicht so gut.

{$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. 

Die Ausgabe lautet:

 
Foo 
42 

42 

Nur die verwalteten Mitglieder Standard für out Parameter initialisiert sind. Sie sollten also die Variable selbst initialisieren, auch wenn sie als out Parameter übergeben wird.

Mehr zu out Parameter finden Sie hier: What's the difference between "var" and "out" parameters?

+0

Also schlagen Sie vor, ** immer ** Call 'Finalize (IPInfo); FillChar (IPInfo, SizeOf (TIPInfo), 0); '? – zig

+0

Meiner Meinung nach sollten Sie den Code so schreiben, dass er für zukünftige Änderungen robust ist. Und das bedeutet, dass der Datensatz immer initialisiert wird, auch wenn er nur verwaltete Typen enthält. Wenn Sie den Datensatz in der Frage aufnehmen, können Sie einen out-Parameter verwenden und sich auf den Compiler verlassen, um init auf der Aufrufseite zu defi- nieren. Aber eines Tages in der Zukunft werden Sie ein nicht verwaltetes Mitglied zum Datensatz hinzufügen, und einige Funktion Meilen entfernt wird brechen. Meiner Meinung nach ist "out" fast nutzlos. –

+1

Ich frage wieder: Kann ich immer 'Finalize (IPInfo); FillChar (IPInfo, SizeOf (TIPInfo), 0); als ** allgemeine ** Lösung? Gibt es überhaupt eine allgemeine Lösung? – zig