2017-08-25 17 views
2

Ich habe eine sehr einfache Anwendung gemacht, aber ich habe ein Problem, das ich wirklich nicht verstehen kann. Schauen Sie sich diese grundlegenden Code:Delphi Fehler bei der Rückgabe TList

unit Unit1; 

interface 

uses 
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, 
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, generics.collections, Vcl.StdCtrls; 

type 
    TForm1 = class(TForm) 
    Button1: TButton; 
    procedure Button1Click(Sender: TObject); 
    procedure FormCreate(Sender: TObject); 
    procedure FormDestroy(Sender: TObject); 
    private 
    { Private declarations } 
    test: TList<integer>; 
    aList: TList<integer>; 
    public 
    { Public declarations } 
    function testGenerics: TList<integer>; 
    end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
test := testGenerics; 
test.Sort; 
showmessage(test[0].tostring); 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
test := TList<integer>.Create; 
aList := TList<integer>.Create; 
end; 

procedure TForm1.FormDestroy(Sender: TObject); 
begin 
aList.Free; 
test.Free; 
end; 

function TForm1.testGenerics: TList<integer>; 
begin 

    aList.Add(4); 
    result := aList; 

end; 

end. 

Grundsätzlich, wenn die Form I öffnet test und aList schaffen werde und dann, wenn ich die Taste die Funktion testGenerics drücken aufgerufen. Warum habe ich die Ungültige Zeigeroperation Fehler?

Ich kann wirklich nicht verstehen, da ich die Objekte (ich denke) richtig erstellen und zerstören. Dieser Code stattdessen funktioniert:

function TForm1.testGenerics: TList<integer>; 
begin 

    Result := TList<integer>.Create; 
    Result.Add(4); 

end; 

In diesem Fall habe ich eine Instanz von TList<integer> kehre zurück, sondern auch in dem Fall, der oben ich eine Instanz von aList kehre zurück (das ist ein TList ist).

Wenn ich im ersten Fall richtig bin, ist test := testGenerics wie test := aList (weil ich aList in der Tat bin wieder) so werde ich test die gleiche Referenz wie aList geben. Hab ich recht?

Antwort

4

Im ersten Beispiel, wenn Sie testGenerics() aufrufen, weisen Sie test erneut auf das Objekt aList zu. Sie verlieren die Spur des ursprünglichen test Objekts, das in dem OnCreate Ereignis erstellt wird, so dass es durchgesickert wird. Und dann im Ereignis OnDestroy, wenn Sie test.Free aufrufen, stürzt es ab, weil Sie bereits das aList Objekt zuvor freigegeben haben, also versuchen Sie, das gleiche Objekt ein zweites Mal freizugeben, was eine ungültige Operation ist.

Im zweiten Beispiel, Sie sind undicht noch die ursprüngliche test Objekt (und jedes TList Sie zuteilen und zu test, mit Ausnahme der letzten zuweisen), aber Sie sind nicht test am aList Objekt zeigen erneut die Zuordnung mehr Daher gibt es im Ereignis OnDestroy keinen Absturz, da beide Variablen auf separate Objekte zeigen.


Was versuchen Sie in erster Linie zu erreichen? Die Rückgabe von Objekten auf diese Weise ist keine gute Übung. Es macht auch keinen Sinn, Sort() auf 1-Element-Listen aufzurufen.

Wenn Sie versuchen, test mit mehreren Werten im Laufe der Zeit zu füllen, sollten Sie test als Eingabeparameter an testGenerics() geben (oder lassen Sie einfach testGenerics() Zugang test direkt über Self), verwenden Sie nicht den Wert Rückkehr überhaupt.

Und auf jeden Fall, loswerden Ihre aList private Mitglied, wie Sie sowieso nichts damit tun.

Versuchen Sie folgendes:

unit Unit1; 

interface 

uses 
    Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, 
    Vcl.Controls, Vcl.Forms, Vcl.Dialogs, generics.collections, Vcl.StdCtrls; 

type 
    TForm1 = class(TForm) 
    Button1: TButton; 
    procedure Button1Click(Sender: TObject); 
    procedure FormCreate(Sender: TObject); 
    procedure FormDestroy(Sender: TObject); 
    private 
    { Private declarations } 
    test: TList<integer>; 
    public 
    { Public declarations } 
    procedure testGenerics(aList: TList<integer>); 
    end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 

procedure TForm1.Button1Click(Sender: TObject); 
begin 
    testGenerics(test); 
    test.Sort; 
    ShowMessage(test[0].tostring); 
end; 

procedure TForm1.FormCreate(Sender: TObject); 
begin 
    test := TList<integer>.Create; 
end; 

procedure TForm1.FormDestroy(Sender: TObject); 
begin 
    test.Free; 
end; 

procedure TForm1.testGenerics(aList: TList<integer>); 
begin 
    // FYI, a better way to exercise Sort() 
    // would be to use RandomRange() instead 
    // of a hard-coded number... 
    aList.Add(4); 
end; 

end. 
+0

Danke Remy! Um das Leck zu beheben, sollte ich das zweite Beispiel verwenden und die Erstellung des Tests in FormCreate entfernen? Dann ist das test.free innerhalb ondestroy gut von dem, was ich undeestood habe –

+0

Das wird nur ein Leck beim ersten Aufruf von 'testGenerics()' verhindern, aber wird immer noch bei nachfolgenden Aufrufen leckt, da Sie 'test.Free' vorher nicht aufrufen Aufruf von 'testGenerics()'. –

Verwandte Themen