Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Verfügbarkeit von Objecten in unterschiedlichen Units (https://www.delphipraxis.net/216756-verfuegbarkeit-von-objecten-unterschiedlichen-units.html)

Ykcim 24. Feb 2025 20:39

Verfügbarkeit von Objecten in unterschiedlichen Units
 
Hallo Zusammen,
ich bin mir nicht sicher, ob der Titel gut gewählt ist. Mir fehlen mal wieder ein paar Grundlagen und ich hoffe, Ihr helft mir weiter.
Ich habe in einem Projekt eine Unit, die heißt TLogic_Procs. In dieser Unit habe ich eine Klasse (TLogic) und in der speichere ich verschiedene Proceduren und Functionen, die nicht projektspezifisch sind und die ich in anderen Projekten auch gut gebrauchen kann.

Sieht vereinfacht so aus:
Delphi-Quellcode:
type
   TWriteServerActivities = procedure (Memo_Name: string; Activity: string) of Object;
   TWriteMailLog = procedure (SG_Name: string; Date, Betreff, LSchein, Empfaenger, Attachm, Result: string) of Object;
   TRows = array of array of string; // [Cols, Rows]
   TCols = array of string;


   TLogic = class
      strict protected

      private
         fWriteServerActivities: TWriteServerActivities;
         fWriteMailActivities: TWriteServerActivities;
         fWriteMailLog: TWriteMailLog;
         //Viele Proceduren
      public
         //Viele Proceduren
         constructor Create(SActivities: TWriteServerActivities = nil; MLog: TWriteMailLog = nil);
         
   end;

   var Logic_Main: TLogic;

implementation

constructor TLogic.Create(SActivities: TWriteServerActivities = nil; MLog: TWriteMailLog = nil);
begin
   if assigned(SActivities) then begin
      fWriteServerActivities:= SActivities;
   end;
   if assigned(MLog) then begin
      fWriteMailLog:= MLog;
   end;
end;


Initialization
   Logic_Main := TLogic.Create;
Finalization
   Logic_Main.Free;
In dem Hauptfenster des Projectes, wo die TLogic_Procs in den uses steht, setze ich im FormShowEvent:
Delphi-Quellcode:
procedure TMainForm.FormShow(Sender: TObject);
var  I: integer;
begin
   Logic_Main.WriteServerActivities:= Write_ServerAcitities;
   Logic_Main.WriteMailActivities:= Write_ServerAcitities;
   Logic_Main.WriteMailLog:= Write_MailLog;
end;
Die Write-Procedure führen dann in dem Hauptfenster Protokoll-Aufgaben durch. Das klappt auch wunderbar, um ehrlich zu sein, besser als ist verstehen kann. Denn nun zu meiner Frage:
Da ich mit mehreren Threads arbeite, erstelle ich oft in Procedure ein neues Object: TLogic (ich gebe es am Ende auch wieder frei). Und egal wo ich bin (die Units haben oft keine Verlinkung untereinander, die TLogic_Procs steht auch dort in den uses), ich kann immer auf Logic_Main zugreifen.

Konkret heiß das, dass ich in der TLogic.Create(Logic_Main.WriteServerActivities) verwenden kann und die Procedure, die ich beim Programmstart der automatisch erstellten Logic_Main Variable zugeordnet habe, wird ausgeführt.

So möchte ich es auch gerne haben, aber ich verstehe ehrlich gesagt nicht, warum das sehr zuverlässig funktioniert. Ich habe immer gedacht, dass wenn eine Unit in den uses steht, dass sie dann wieder neu created wird, aber warum steht dann die Procedure aus dem Hauptfenster zur Verfügung.

Hintergrund ist, dass ich gerade an einer Klasse bastle, bei der ich auch Daten aus einem zentralen Object in neu angelegte Objecte (und zurück) übergeben möchte. Und da möchte ich es gerne "richtig" machen...

Meine Theorie ist, dass das Object Logic_Main einen Pointer bekommt und dass wenn die Unit TLogic_Procs in einer anderen Unit in den uses steht, sie zwar neu wieder erstellte wird, aber dass das "neue" Object Logic_Main wieder den gleichen Pointer erhält und somit das "gleiche" Object ist. Aber das ist nur eine Theorie...

Könnt Ihr mir hier Klarheit verschaffen?

Vielen Dank
Patrick

jaenicke 24. Feb 2025 21:25

AW: Verfügbarkeit von Objecten in unterschiedlichen Units
 
Zitat:

Zitat von Ykcim (Beitrag 1546525)
So möchte ich es auch gerne haben, aber ich verstehe ehrlich gesagt nicht, warum das sehr zuverlässig funktioniert. Ich habe immer gedacht, dass wenn eine Unit in den uses steht, dass sie dann wieder neu created wird, aber warum steht dann die Procedure aus dem Hauptfenster zur Verfügung.

Die Unit ist lediglich ein Container. Der Eintrag in der uses heißt nur, dass du im Quelltext auf dort deklarierte Typen, Konstanten und Variablen zugreifen kannst, mehr nicht. Erzeugt wird dadurch nichts. Das ist nur die Zugriffsmöglichkeit.

Es werden lediglich beim Starten der Anwendung die initialization und beim Beenden die finalization Sektion ganz unten in der Unit ausgeführt, ebenso wie Klassenkonstruktoren und -destruktoren. Dabei werden immer zuerst die Units in der uses initialisiert und dann die Unit selbst. Das ist der Grund, dass das so funktioniert.

Die Funktionen aus dem Hauptfenster weist du ja selbst von außen zu. Wo die deklariert sind, weiß die Unit gar nicht. Die bekommt nur die Adresse und kann sie so aufrufen.

Medium 24. Feb 2025 21:28

AW: Verfügbarkeit von Objecten in unterschiedlichen Units
 
Zitat:

Zitat von Ykcim (Beitrag 1546525)
Ich habe immer gedacht, dass wenn eine Unit in den uses steht, dass sie dann wieder neu created wird

Das hier ist der Denkfehler. Mit "uses" gibst du nur an, dass du in der Unit in der du das "uses XYZ" hin schreibst, auf öffentliche Member von XYZ zugreifen möchtest. Diese sind identisch, egal von wo du letztlich drauf zugreifst. Da wird nichts neu erstellt; alles zeigt auf die selben Daten und Codestellen wie in XYZ direkt.

berens 24. Feb 2025 23:30

AW: Verfügbarkeit von Objecten in unterschiedlichen Units
 
So habe ich das bei mir gelöst:

Delphi-Quellcode:
type
  TMeinObjekt = class(TComponent)
  ...

  function MeinObjekt: TMeinObjekt;

implementation

var
  // Edit: Danke für den Hinweis!
  FMeinObjekt: TMeinObjekt = NIL;

function MeinObjekt: TMeinObjekt;
begin
  Result := NIL;
  try
    if not assigned(FMeinObjekt) then begin
      FMeinObjekt := TMeinObjekt.Create(NIL);
    end;
    Result := FMeinObjekt;
  except
    on E: SysUtils.Exception do begin
      ...
    end;
  end;
end;

...


initialization
begin
  // In der Tat unnötig
  FMeinObjekt:= NIL;
end;

finalization
  FreeAndNil(FMeinObjekt);
Keine Ahnung, ob das der beste Weg ist, aber beim Initialisieren der Unit wird FMeinObjekt explizit ungültig gemacht (NIL), dass es nicht auf irgendeinen zufälligen Arbeitsspeicherbereich verweist, und dieser dann versehentlich als FMeinObjekt behandelt wird.

Die function MeinObjekt: TMeinObjekt; sorgt dafür, dass du immer ein gültiges Objekt erhältst, und dass es (über diesen Weg) auch immer nur eine Instanz gibt.

Wird das Programm beendet, wird das Objekt im Finalization-Abschnitt korrekt freigegeben.

Benötigt ein anderes Programm dieses Objekt nicht, wird es nicht erzeugt. Wird es benötigt, wird es automatisch erzeugt. Wird es erzeugt, wird es automatisch freigegeben.

Du darfst/solltest halt an keiner anderen Stelle im Quellcode weitere Instanzen davon erzeugen mit TMeinObjekt.Create, ansonsten sollte alles passen?

https://de.wikipedia.org/wiki/Singleton_(Entwurfsmuster)

Ykcim 25. Feb 2025 07:39

AW: Verfügbarkeit von Objecten in unterschiedlichen Units
 
Guten Morgen Zusammen,

vielen Dank für Eure Antworten und Erklärungen!

@jaenicke und @Medium
Ich habe mich falsch ausgedrückt, ich meinte nicht, dass create ausgeführt wird, sondern der Initialzation-Teil. Aber ich habe Euch verstanden, dass der nur ausgeführt wird, wenn die App gestartet wird und nichts mit der Benennung in des uses zu tun hat.
Und da ich in der TLogic ja in der Initialization Logic:Main:= TLogic.Create verwende, steht Logic_Main der App zur Verfügung.
Das eigentlich interessante ist, dass Logic_Main in der TLogic_Procs erstellt wird und dass ich auf dieses Object von jeder Unit aus zugreifen kann, in der TLogic_Procs in den uses steht. Das erklärt, warum es funktioniert und eröffnet mir ganz neue Möglichkeiten.

@berens
Vielen Dank für das Bespiel. Ich denke aber, dass ich es für dieses Object nicht verwenden kann. Ich verwende in vielen Procedure und Funktionen ein Object von TLogic und ich habe mir angewöhnt, immer ein solches zu createn und im Finally-Teil wieder freizugeben. Damit kann ich Proceduren und Funktionen auch in Threads einsetzen, ohne das es knallt. Aber für andere Objecte könnte das sehr interessant sein.

Vielen Dank und habt einen guten Tag
Patrick

fisipjm 25. Feb 2025 07:53

AW: Verfügbarkeit von Objecten in unterschiedlichen Units
 
Zitat:

Zitat von berens (Beitrag 1546529)
So habe ich das bei mir gelöst:

Delphi-Quellcode:
initialization
begin
  FMeinObjekt:= NIL;
end;

finalization
  FreeAndNil(FMeinObjekt);

Der initialization Teil ist in meinen Augen Sinnlos, korrigiert mich bitte wenn ich Falsch liege :)
Hier wird kein Create durchgeführt sondern ein Nil Pointer auf ein Objekt gesetzt, dass es eigentlich noch nicht geben dürfte. Es sei den, du erzeugst das Objekt direkt beim Programmstart (Quelltext des Projekts selbst und nicht in der Unit). Selbst dann müsste es aber "leer" sein.

Hobbycoder 25. Feb 2025 08:47

AW: Verfügbarkeit von Objecten in unterschiedlichen Units
 
Zitat:

Zitat von Ykcim (Beitrag 1546531)
Guten Morgen Zusammen,

vielen Dank für Eure Antworten und Erklärungen!

@jaenicke und @Medium
Ich habe mich falsch ausgedrückt, ich meinte nicht, dass create ausgeführt wird, sondern der Initialzation-Teil. Aber ich habe Euch verstanden, dass der nur ausgeführt wird, wenn die App gestartet wird und nichts mit der Benennung in des uses zu tun hat.
Und da ich in der TLogic ja in der Initialization Logic:Main:= TLogic.Create verwende, steht Logic_Main der App zur Verfügung.
Das eigentlich interessante ist, dass Logic_Main in der TLogic_Procs erstellt wird und dass ich auf dieses Object von jeder Unit aus zugreifen kann, in der TLogic_Procs in den uses steht. Das erklärt, warum es funktioniert und eröffnet mir ganz neue Möglichkeiten.

@berens
Vielen Dank für das Bespiel. Ich denke aber, dass ich es für dieses Object nicht verwenden kann. Ich verwende in vielen Procedure und Funktionen ein Object von TLogic und ich habe mir angewöhnt, immer ein solches zu createn und im Finally-Teil wieder freizugeben. Damit kann ich Proceduren und Funktionen auch in Threads einsetzen, ohne das es knallt. Aber für andere Objecte könnte das sehr interessant sein.

Vielen Dank und habt einen guten Tag
Patrick

Vielleicht interessant für dich:
Delphi-Quellcode:
https://www.youtube.com/watch?v=I15UFEIfSao

berens 25. Feb 2025 09:47

AW: Verfügbarkeit von Objecten in unterschiedlichen Units
 
Zitat:

Zitat von fisipjm (Beitrag 1546532)
Der initialization Teil ist in meinen Augen Sinnlos, korrigiert mich bitte wenn ich Falsch liege :)
Hier wird kein Create durchgeführt sondern ein Nil Pointer auf ein Objekt gesetzt, dass es eigentlich noch nicht geben dürfte. Es sei den, du erzeugst das Objekt direkt beim Programmstart (Quelltext des Projekts selbst und nicht in der Unit). Selbst dann müsste es aber "leer" sein.

Moin,
tja, ich leide an Fehler-Paranoia :lol:

Tatsache ist, das es für mich sehr "diffus" ist, wann/ob Delphi Variablen richtig initialisiert. Der Arbeitsspeicher (RAM) deines Computers wird ja wiederverwendet: Wenn du ein Programm zumachst, wir der Arbeitsspeicher frei, in dem Sinne von "Keine Referenz zeigt mehr darauf", aber der RAM wird -meines Wissens(!) nicht "Formatiert", so dass nach Programmende der komplette Arbeitsspeicher auf 0000000 steht, sondern es bleibt zunächst mal alles so wie es gerade ist. Wenn du nun in Delphi neue Variablen und Objekte hast, die einen Zeiger auf den Arbeitsspeicher bekommen mit "den darfst du benutzen, da arbeitet aktuell niemand mit", bedeutet das nicht, dass nun der komplette Arbeitsspeicher auf 0 gesetzt wird, sondern dass dort immernoch ein Buchstabensalat stehen *könnte*, und somit in einer brandneuen, uninitialisierten integer Variable der Wert 17 steht, in einem String "adfkgjzudfdui", und in bei einem Zeiger auf ein Objekt dieses bei Assigned(MeinObjekt)=True zurück geben könnte.

Wie gesagt, das ist nur die Paranoia. In der Delphi Hilfe finde ich bei den einzelnen Datentypen (Integer, TComponent, String, ...) NICHT, *ob* die Variablen immer initilaisiert werden (string='' integer=0 TMeinObjekt=NIL) und wenn ja, mit welchem Wert.

Damit ich nicht böse überrascht werde, oder in einer neuen Delphi Version auf einmal alles gehandhabt wird (Hey, deine widestring-variable ist nun beim Programmstart nicht mehr '' sonder NIL!) initialisiere ich lieber 100x unnötig alles per Hand, kann dann aber sicher sein, dass alles wirklich so ist, wie erwartet.

Also: Ja, beim Programmstart wird die MeinObjekt (unnötig) auf NIL gesetzt - damit stelle ich sicher, dass assigned() in der entsprechenden Methode auch wirklich 100% sicher sagen kann, ob das Objekt *gültig* existiert, oder nicht. Wie gesagt, assigned(MeinObjekt) könnte(?) evtl. true bei sein, obwohl ich noch kein Objekt erzeugt habe. Die Funktion erzeugt dann das gültige Objekt und gibt es zurück.

Ja, wahrscheinlich alles komplett übertrieben und unnötig, aber ich habe mit Delphi einfach schon viel zu viel Bulls*** erlebt, als dass mich sowas noch wundern würde.

Zeigt mir gerne wo steht, dass *ALLE* Variablen und Objekte stets mit 100% Zuverlässigkeit mit NIL, 0 oder "" initialisiert werden.
--> Danke für den Link, ich hatte bei "Integer" und "Einfache Typen" gesucht, aber bei "Variablen" war zu einfach :lol:. Tatsächlich trifft das aber dennoch nur auf globale, nicht auf lokale Variablen zu.

ChatGPT sagt dazu:
Zitat:

In Delphi werden globale Variablen und Felder von Objekten (also Klasseninstanzvariablen) automatisch mit nil initialisiert. Lokale Variablen, wie in deinem Beispiel die Variable b in der Prozedur, werden dagegen nicht automatisch auf nil gesetzt, wenn du sie nicht explizit initialisierst. Das bedeutet:

Globale Variablen und Klassenfelder: Immer nil, wenn sie noch keinen gültigen Wert erhalten haben.
Lokale Variablen (in Prozeduren/Funktionen): Können uninitialisiert sein und daher zufällige (Garbage‑)Werte enthalten, die nicht nil sind.

Die offizielle Embarcadero-Dokumentation zum Verhalten der Funktion Assigned und der Speicherinitialisierung bestätigt, dass nur Variablen, die explizit initialisiert wurden (oder als globale/Membervariablen deklariert sind), sicher den Wert nil besitzen. Wird eine lokale Variable nicht initialisiert, kann ein zufälliger Speicherwert vorliegen, wodurch Assigned(b) unter Umständen true zurückgeben kann.
Leider hat das der Link gefehlt.

Okay, also globale Variablen und Objekte scheinen das Problem nicht zuhaben, lokale schon. Was soll das denn bitte?
Gut, mein initialisieren ist unnötig, aber ich kotze im Strahl mit den 1000 Sonderfällen und Ausnahmeregelungen in Delphi wann was geht oder nicht geht, oder wann was automatisch passier und wann nicht. So ist es mehr Tipp-Arbeit und unnötig, aber zumindest ist damit meine Speicherverwaltung konsistent für lokale und globale Variablen. :stupid:

Ich hätte Schreiner werden sollen... :roll:

Uwe Raabe 25. Feb 2025 10:01

AW: Verfügbarkeit von Objecten in unterschiedlichen Units
 
Man kann globale Variablen auch gleich bei der Deklaration initialisieren:
Delphi-Quellcode:
var
  FMeinObjekt: TMeinObjekt = nil;

jaenicke 25. Feb 2025 10:27

AW: Verfügbarkeit von Objecten in unterschiedlichen Units
 
Zitat:

Zitat von berens (Beitrag 1546538)
Zeigt mir gerne wo steht, dass *ALLE* Variablen und Objekte stets mit 100% Zuverlässigkeit mit NIL, 0 oder "" initialisiert werden.

Das ist eindeutig dokumentiert:
https://docwiki.embarcadero.com/RADS...ables_(Delphi)
Zitat:

If you do not explicitly initialize a global variable, the compiler initializes it to 0. Object instance data (fields) are also initialized to 0. On the Wiin32 platform, the contents of a local variable are undefined until a value is assigned to them.

When you declare a variable, you are allocating memory which is freed automatically when the variable is no longer used. In particular, local variables exist only until the program exits from the function or procedure in which they are declared.
Anders sieht es bei referenzgezählten Datentypen aus (strings, Interfaces, ...). Die werden auch lokal mit 0 initialisiert, was einfach daran liegt, dass ansonsten bei der ersten Zuweisung versucht würde, den alten Speicher freizugeben (obwohl der Wert gar nicht gültig wäre).


Alle Zeitangaben in WEZ +1. Es ist jetzt 02:19 Uhr.
Seite 1 von 2  1 2      

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