Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Self nicht definiert nach dem Create? (https://www.delphipraxis.net/214578-self-nicht-definiert-nach-dem-create.html)

looseleaf 2. Feb 2024 10:12

Self nicht definiert nach dem Create?
 
Liebe Gemeinde,

wir verwenden in unserem Programm immer wieder quasi-statische Execute() Methoden, um Froms zu ersellen und am Ende wieder freizugeben. Nach diesem Muster:

var MeinForm: TMeinForm;

function TMeinForm.Execute(): Boolean;
begin
Result := false;
MeinForm := TMeinForm.Create(Application);
With MeinForm do
try
// tuwas
Result := ShowModal=mrOk;
finally
Free;
end
end;


Wenn wir hier beim // tuwas auf Self referenzieren, ist das eine blöde Idee, oder? Ich glaube auch zu verstehen warum: Execute() ist hier noch keine Methode des Objekes, daher ergibt Self keinen Sinn. Was ich aber nich verstehe: Bei einem zweiten Aufruf funkioniert der Zugriff auf Self, obwohl am Ende ein Free steht. Kann mir wer auf die Sprünge helfen, warum das beim zweiten Mal geht? Und das Self zeigt lt. Debugger auch ab der Zeile mit dem .Create() auf dieselbe Adresse wie MeinForm.

Wir verwenden hier die MeinForm, dann funktioniert's wie erwartet.

Fragt bitte nicht, warum hier eine Variable verwendet wird, das ist Code von vor über 20 Jahren.

Danke
Stefan

Kas Ob. 2. Feb 2024 10:41

AW: Self nicht definiert nach dem Create?
 
Hi,

The reason is first time it fail because Self is nil (not defined), and that because of this global var
Code:
var MeinForm: TMeinForm;
Initially it is nil (zeroed), after creating it once, it will held a valid value and full MienForm structure/object, BUT after freeing it, Delphi Memory Manager (FastMM) most likely will save time and not clear the allocated memory hence the structure of the TMainForm is reserved and pointing to a valid VMT, also you are free the global without nil (zeroing) hence it still point to a valid to read but not valid to be trusted pointer to memory/data/object/class/structure...

If you enable Memory Debugging tools then it will point that this is case of use after free, i mean the second call.
Also you can simply nil that var and get failure on the second time, a consistent failure as it should be.

Also this is hidden and dangerous bug as accessing Self second time could lead to undefined behavior as the memory from MM point of view is freed and empty and can be reused, this can lead to all sort of unexplained exception and no sense stack traces....

The simplest way to see this exception and protect against it is
1) Don't use global var !
2) In this case im my opinion is a legitimate place to use FreeAndNil(MeinForm); instead of MeinForm.Free;
3) Before calling MeinForm := TMeinForm.Create(Application); you really should check for Assigned(MeinForm) then raise an exception, that Create should only happen if MeinForm already created to prevent memory corruption.
..

BigAl 2. Feb 2024 10:41

AW: Self nicht definiert nach dem Create?
 
"Self" geht bei statischen Methoden nicht. Das ist ja quasi eine globale Funktion. Anstelle "Self" würde ich immer "MeinForm" verwenden.

Grundsätzlich würde ich - wenn es schon so gemacht sein soll - das "MeineForm" als lokale Variable der Funktion definieren:

Delphi-Quellcode:
function TMeinForm.Execute(): Boolean;
var
  MeinForm: TMeinForm;
begin
  Result := false;
  MeinForm := TMeinForm.Create(Application);
  With MeinForm do
  try
    // tuwas
    Result := ShowModal = mrOk;
  finally
    Free;
  end
end;
Auf "with" würde ich ganz verzichten. Das "with" habe ich vor 20 Jahren oder aus meinem Repertoir gestrichen. Das macht den Code schwer zu lesen und teilweise zu debuggen:

Delphi-Quellcode:
function TMeinForm.Execute(): Boolean;
var
  MeinForm: TMeinForm;
begin
  Result := false;
  MeinForm := TMeinForm.Create(Application);
  try
    // tuwas
    Result := MeinForm.ShowModal = mrOk;
  finally
    MeinForm.Free;
  end
end;

jaenicke 2. Feb 2024 12:21

AW: Self nicht definiert nach dem Create?
 
Zitat:

Zitat von looseleaf (Beitrag 1532924)
wir verwenden in unserem Programm immer wieder quasi-statische Execute() Methoden, um Froms zu ersellen und am Ende wieder freizugeben.

Wie wäre es, daraus echte statische Methoden zu machen? Also class function statt function? Dann kannst du Self gar nicht fälschlicherweise verwenden.

Dazu noch wie bereits vorgeschlagen nur eine lokale Variable für das Formular und kein with, dann kann schon nicht mehr viel schiefgehen.

Zitat:

Zitat von looseleaf (Beitrag 1532924)
Und das Self zeigt lt. Debugger auch ab der Zeile mit dem .Create() auf dieselbe Adresse wie MeinForm.

Der Debugger kann mit with nichts anfangen. Insofern musst du in der Konstellation sehr vorsichtig sein, wenn du mit dem Debugger arbeitest. Das with wird dort einfach ignoriert, weshalb da sehr wilde Daten herauskommen können.

Im Grunde kann man Stellen mit with nicht sinnvoll debuggen.

looseleaf 2. Feb 2024 14:11

AW: Self nicht definiert nach dem Create?
 
Danke für eure Antworten, mir ist ehrlicherweise nicht ganz klar, warum das überhaupt zulässig ist, aber wird wohl daran liegen, dass die Methode nicht als class function definiert ist.

Mein Chef hat diese Art des Codens irgendwann Ende der 90er in einem Forum gefunden und übernommen - funktioniert ja auch. Meistens :)

Ich würde hier ohnehin für ein sauberes Singleton plädieren mit einer getMeinForm() und ohne globaler Variable...

Wir werden unseren Code durchforsten, ob wir noch bei anderen Formularen so einen Schmonzes stehen haben

Schönes Wochenende,
Stefan

Incocnito 2. Feb 2024 15:58

AW: Self nicht definiert nach dem Create?
 
Tipp:
Ändere die Funktion mal ab auf
Delphi-Quellcode:
class function Execute() : Boolean;
bzw. unten auf
Delphi-Quellcode:
class function TMeinForm.Execute() : Boolean;
.
class function / class procedure wäre für deinen Anwendungsfall logischer,
da du vom Objekt keine Instanz brauchst.
Dort müsste er auch meckern "was ist Self?".

Liebe Grüße aus dem Norden
Incocnito

Andreas L. 2. Feb 2024 16:34

AW: Self nicht definiert nach dem Create?
 
Zitat:

Zitat von Incocnito (Beitrag 1532938)
Tipp:
Ändere die Funktion mal ab auf
Delphi-Quellcode:
class function Execute() : Boolean;
bzw. unten auf
Delphi-Quellcode:
class function TMeinForm.Execute() : Boolean;
.
class function / class procedure wäre für deinen Anwendungsfall logischer,
da du vom Objekt keine Instanz brauchst.
Dort müsste er auch meckern "was ist Self?".

Liebe Grüße aus dem Norden
Incocnito

So ähnlich halte ich es auch immer:
Delphi-Quellcode:
type
  TMyForm = class(TForm)
    //...
  public
    class function Execute: Boolean;
  end;

class function TMyForm.Execute: Boolean;
var
  FormDlg: TMyForm;
begin
  FormDlg := TMyForm.Create(Application);
  try
    // ggf. irgendwas vor dem Öffnen des Dialogs machen
    FormDlg.LoadOptions;

    // Dialog öffnen
    Result := FormDlg.ShowModal = mrOK;

    // ggf. nach dem Schließen des Forms irgendwas speichern
    if Result then
      FormDlg.SaveOptions;
  finally
    FreeAndNil(FormDlg);
  end;
end;

himitsu 2. Feb 2024 16:36

AW: Self nicht definiert nach dem Create?
 
Nein, Class-Function kennt auch ein Self, nur ist es dort keine Instanz (TObject), sondern ein Typ (TClass).

Eine Static-Class-Function kennt kein Self.


Aber alles egal, da Self nur ein unsichtbarer Parameter an Klassenmethoden ist
und in einem WITH sowieso nicht existiert.


Abgesehn davon, dass man WITH nach Möglichkeit eh nicht benutzen sollte.


Alle Zeitangaben in WEZ +1. Es ist jetzt 00:46 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz