Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi globale Variablen (https://www.delphipraxis.net/129240-globale-variablen.html)

AlexanderBrade 15. Feb 2009 10:52


globale Variablen
 
Jetzt hab ich eine ganz banale FRage, aber irgendwie komme ich trotzdem nicht voran:

Wenn ich im Formular1 die Variable sp auf 2 setze, wie schaffe ich es, dass er sich das bis zum 7.Formular "merkt".
D.h. ich habe die Variable in beiden FOrmularen global(also 2x) definiert, ist das der Fehler, denn normalerweise verschwindet doch keine Varibale aus dem Speicher?!?

Vielen Dank im voraus.

DeddyH 15. Feb 2009 10:55

Re: globale Variablen
 
Ganz allgemein sollte man keine globalen Variablen verwenden, wenn es nicht notwendig ist. Aber versuch mal, mittels
Delphi-Quellcode:
Unitname.Variablenname
zuzugreifen, damit Delphi genau weiß, welche gemeint ist. Voraussetzung ist allerdings, dass die Variable im interface- und nicht im implementation-Abschnitt deklariert wurde.

Matze 15. Feb 2009 10:56

Re: globale Variablen
 
Hallo,

ja das ist der Fehler. Du darfst sie nicht 2 Mal deklarieren.
Wenn du die Variable bei "var Form1: TForm" deklarierst und die Unit1 in Unit7 über die "uses" einbindest, müsstest du die Variable nutzen können. Du kannst sie aber auch unter "public" von Unit1 deklarieren und über "Form1.Variable" im Unit7 darauf zugreifen, wenn die Unit dort eingebunden wurde.
Ich hoffe, ich erzähle gerade keinen Mist. ;)

Grüße, Matze

AlexanderBrade 15. Feb 2009 10:59

Re: globale Variablen
 
wenn sie im interface teil stehen soll wo muss sie dann deklariert werden?

DeddyH 15. Feb 2009 11:19

Re: globale Variablen
 
Unter var

AlexanderBrade 15. Feb 2009 11:58

Re: globale Variablen
 
Wenn ich das mache, sagt der Compiler in Form7 bei der Varibale, z.B. form1.sp undefinierter Bezeichner, unter public nimmt er es, liest die Variable aber auch nicht richtig aus!

DeddyH 15. Feb 2009 12:06

Re: globale Variablen
 
Unitname, nicht Formname, Du musst schon genau lesen, was wir hier schreiben.

[edit] Wobei es schon besser wäre, Properties zu definieren, da warst Du schon auf einem guten Weg. [/edit]

Sir Rufo 15. Feb 2009 12:46

Re: globale Variablen
 
Also wenn ich globale Variablen brauche ... dann packe ich die mir in eine Klasse in einer eigenen Unit.

Diese Unit zu jeder anderen Unit hinzugefügt und gut ist ...

Damit ists global und trotzdem OOP :mrgreen:

cu

Oliver

DeddyH 15. Feb 2009 12:48

Re: globale Variablen
 
Aber keine globale Variable mehr im eigentlichen Sinne. OK, die Klasseninstanz könnte man dann als globale Variable definieren :mrgreen:

Sir Rufo 15. Feb 2009 13:37

Re: globale Variablen
 
genau so ;)
Delphi-Quellcode:
unit GlobalData;

interface

type
  TGlobalData = class
  ...
  end;

var
  MyGlobalData : TGlobalData;

implementation

...

initialization

  MyGlobalData := TGlobalDate.Create;

finalization

  MyGlobalData.Free;

end.

mkinzler 15. Feb 2009 13:42

Re: globale Variablen
 
Dann hast du mehrere gloable Variablen durch eine ersetzt, aber hast trotzdem noch eine globale Variable :zwinker:

Sir Rufo 15. Feb 2009 13:43

Re: globale Variablen
 
logisch, ich brauche einen gemeinsamen Aufhänger ... sonst funzt das ja nicht.

BTW: Jedes erstellte Form ist als globale Variable definiert ... ;)

cu

Oliver

Tharon 15. Feb 2009 15:47

Re: globale Variablen
 
Hi @all!

Dann gebe ich hier auch mal meinen Senf dazu, denn dieses Thema scheint ja immer noch viele Fragen aufzuwerfen ;-) Auch Borland macht es nicht unbedingt toll vor, weshalb man sich natürlich auch nicht wundern darf, dass gerade viele Anfänger immer wieder die gleichen Fehler machen ;-)

Also... globale Variablen? Generell nein!

Aber... wie bei jeder Regel gibt es Ausnahmen ;-) Und eigentlich ist auch die Frage nach "globalen Variablen" nicht wirklich sinnvoll gestellt, denn es kommt letztendlich darauf an, wie diese deklariert und verwendet werden und wie deren Wert gesetzt/verändert werden kann.

Der Ansatz von Sir Rufo ist ja schon ganz gut... immerhin werden hier schon mal alle globalen Variablen innerhalb einer Klasse gekapselt. Er verwendet zwar einen OOP-Ansatz, dies ist allerdings hier eher eine "Formalität", denn die eigentlichen Probleme, die globale Variablen verursachen, werden nicht gelöst:
  • Mehrfache Instanziierung: Die Klasse TGlobalData kann mehrfach instanziert werden, an beliebigen Stellen im Quellcode.
  • Keine Kontrolle über die Werte: Die Eigenschaften dieser Instanz (entsprechen den "stand alone"-Variablen) können beliebig von jedem Quelltext geändert werden.
Das erste Problem der mehrfachen Instanziierung entspricht genau dem Problem, das AlexanderBrade hatte - er hatte 2 mal die gleiche globale Variable deklariert.

Die Lösungen für diese Probleme sind recht einfach, wenn man den Ansatz von Sir Rufo konsequent weiter verfolgt.

Mehrfache Instanziierung verhindern

Die mehrfache Instanziierung muss verhindert werden - hierfür gibt es ein einfaches Design Pattern: Singleton. Dieses Pattern bewirkt, dass eine Klasse nur ein einziges mal instanziiert werden kann - und das bezeichnet man als "Singleton".
Wie mkinzler schon richtig anmerkte, gäbe es tatsächlich immer noch eine globale Variable für ein Objekt der Klasse TGlobalData, durch die Implementierung als Singleton entfällt nun aber die Notwendigkeit für eine globale Variable! :-)
Jeder Code, der Zugriff auf die gobalen Daten benötigt, fordert über eine Klassenmethode des Singleton (z.B. RetrieveInstance) die (einzige) globale Instanz dieser Klasse an.

Ohne jetzt zu sehr auf alle Details des Singleton Patterns eingehen zu wollen (einfach mal Suchen nach Singleton): der "Trick" besteht darin, dass es keinen öffentlichen Konstruktor gibt, um die beliebige Instanziierung zu verhindern. In Delphi wird einfach der immer vorhandene öffentliche Konstruktor "deaktiviert", indem dieser eine Exception auslöst. Statt dessen implementiert man einen privaten Konstruktor, der von einer Klassenmethode RetrieveInstance aufgerufen wird. Dieser private Konstruktor ruft einfach den geerbten Konstrultor auf.
Die Klassenmethode RetrieveInstance überprüft zunächst, ob bereits eine Instanz existiert und erzeugt ggf. eine Instanz (falls noch nicht vorhanden) über den privaten Konstruktor und liefert diese zurück. Die Klassenmethode RetrieveInstance ist also der Ersatz für den öffentlichen Konstruktor und somit hat die Klasse die volle Kontrolle über ihre Instanziierung.

In meinen Projekten verwende ich eine Singleton-Klasse TGlobalRessources:

Delphi-Quellcode:
unit GlobalRessources;

...

TGlobalRessources = class
  private
    // ...
    constructor _Create; virtual;
  public
    constructor Create; virtual;
    class function RetrieveInstance(): TGlobalRessources;
    // ...
end;


implementation


var
  m_Instance:   TGlobalRessources;


constructor TGlobalRessources._Create;
begin
  inherited Create;
end;


constructor TGlobalRessources.Create;
begin
  raise Exception.Create('Trying to instantiate singleton class');
end;


function TGlobalRessources.RetrieveInstance();
begin
  if (m_Instance <> nil) then
  begin
    m_Instance := _Create();
  end;

  Result := m_Instance;
end;


initialization


m_Instance := nil;

// oder, wenn sofortige Instanziierung sinnvoll ist:

m_Instance := TGlobalRessources.RetrieveInstance();


finalization


if (m_Instance <> nil) then
begin
  FreeAndNil(m_Instance);
end;
Ich hoffe, ich habe hier jetzt keine Fehler eingebaut, denn ich habe das jetzt mal eben schnell hier hin getippt, ohne in meinem Code nachzuschauen. Aber es ging mir ja auch nur darum, das Prinzip zu verdeutlichen ;-)


Kontrolle über die Variablen-Werte

Schön... jetzt hat man ein globales Objekt, das auch ganz sicher nur ein einziges mal existiert... aber trotzdem kann jetzt immer noch jeder beliebige Code willkürlich die Eigenschaften dieses Objektes (globale Daten) verändern...

Die Lösung dieses Problems ist noch einfacher und lautet: Eigenschaften und Zugriffsmethoden ;-)

Wenn man konsequent Zugriffsmethoden für alle Eigenschaften verwendet, erreicht man zumindest schon mal, dass die Wertzuweisungen (natürlich auch das Auslesen) selbst unter der Kontrolle der betreffenden Klasse stehen. Allerdings sollte man bei einer solchen Klasse noch einen Schritt weiter gehen und nach Möglichkeit nur "read only"-Eigenschaften verwenden, also Eigenschaften ohne write-Methode.

Als typisches Beispiel nehme ich mal den am Programm angemeldeten Benutzer. Man könnte nun diverse Properties für den Bennutzer deklarieren (wie UserID, UserName, UserIsAdmin, etc.). Diese Eigenschaften werden dann z.B. von einem Login-Formular gesetzt. Das Problem ist, dass diese Eigenschaften ausser vom Login-Formular auch von jedem beliebigen anderen Code gesetzt werden können und das auch noch unabhängig voneinander!
Beispielsweise könnte man auf die Idee kommen, eine Funktion zu implementieren, die aus dem Programm heraus die Möglichkeit bietet, die Anmeldung zu ändern. Der Entwickler implementiert hierzu vielleicht ein eigenes Formular und verwendet nicht das eigentliche Login-Formular. Außerdem setzt er vielleicht nur die Eigenschaft UserID und "vergisst", dass hiervon noch weitere Eigenschaften abhängen --> "bumm" ;-)

Hier sollte man also besser für den angemeldeten Benutzer ein eigenes Objekt vorsehen (bei mir vom Typ TUserProfile). Dieses Objekt UserProfile ist eine öffentliche Eigenschaft der Global Ressorce und kann nur gelesen werden.
Die oben erwähnten Eigenschaften UserID, UserName, UserIsAdmin, etc. sind nun Eigenschaften der Klasse TUserProfile und können ebenfalls nur gelesen werden. Für die Anmeldung stellt die Klasse TUserProfile eine Methode Login zur Verfügung. Ebenso eine Methode, um das Kennwort zu ändern (ChangePwd).

Damit ist folgendes sichergestellt:
  • Es gibt nur ein einziges UserProfile-Objekt, da dieses eine Eigenschaft des Singletons TGlobalRessources ist.
  • Das UserProfile-Objekt selbst kann nicht verändert werden, da es eine "read only"-Eigenschaft des TGlobalRessources-Objektes ist.
  • Die Eigenschaften des angemeldeten Benutzers (UserID, UserName, UserIsAdmin, etc.) sind immer konsistent, da diese nur intern über die Methode Login gesetzt werden.

So.. und jetzt beende ich mal meine Ausführungen... ist ja schon fast ein Tutorial geworden ;-)

Liebe Grüße

Cyf 15. Feb 2009 15:58

Re: globale Variablen
 
Warum eine Instanz statt Klassenvariablen verwenden? Und warum überhaupt eine Klasse (außer man möchte natürlich einen Zugriffsschutz über Properties), eine einzige Unit mit allen globalen Variablen tut es imho auch, solange man die Variablen immer mit Unitnamen anspricht und ob es nun Klasse.X oder Unit.X ist, spielt auch keine Rolle.

mkinzler 15. Feb 2009 16:01

Re: globale Variablen
 
Da (Object-)Pascal eine Hybridsprache ist, welche aus einer prozeduralen Sprache entstanden ist, ist es nicht möglich auf gloable Variablen zu verzichten.

Reinhard Kern 16. Feb 2009 11:16

Re: globale Variablen
 
Zitat:

Zitat von mkinzler
Da (Object-)Pascal eine Hybridsprache ist, welche aus einer prozeduralen Sprache entstanden ist, ist es nicht möglich auf gloable Variablen zu verzichten.

Hallo,

ich finde es auch überhaupt nicht sinnvoll: wenn ich z.B. ein Programm zur Dateibearbeitung habe, das genau 1 Datei zur Zeit bearbeitet, dann SIND z.B. die Datei und der Dateiname globale Variablen, und solange ich beim Programmieren des Denkens noch mächtig bin, sehe ich auch garkeinen Grund, diese Tatsache mit fragwürdigen OOP-Konstrukten zu verstecken, nur um das Dogma "niemals global" einzuhalten. Delphi selbst definiert wichtige globale Variablen, z. B. Application.

Nachteilig ist nur, dass Borland es versäumt hat, einen echten globalen Namespace zu schaffen (z.B. durch eine var-Deklaration im dpr-file, oder meinetwegen auch mit "global"), so dass man nicht einfach mit Bordmitteln auf Mehrfachinstanziierung prüfen kann (oder diese von vornherein verhindert wird).

Gruss Reinhard

p80286 16. Feb 2009 12:54

Re: globale Variablen
 
Hallo zusammen,

wenn ich mich richtig erinnere sind "globale" Variablen PfuiBa weil zu viele Nebeneffekte auftreten können z.B. bei Laufvariablen wie i,j,k.....
Grundsätzlich sollten alle Variablen nur innerhalb einer Routine gültig sein. Und die Übergabe nur über die "offizielle" Schnittstelle erfolgen.
Meiner Meinung nach ist es MANCHMAL sinnvoll in einer "globalen" Variablen Informationen zu hinterlegen, die in der ganzen Anwendung benötigt werden. Das diese Notwendigkeit von dem einen oder anderen cleveren Codebastler mißbraucht wurde, steht auf einem ganz anderen Blatt.
Und wenn schon global, dann sollte man(n) tunlichst vermeiden wild aus einer Subroutine heraus darauf zuzugreifen. (Auch wenn es im Prinzip möglich wäre)

Gruß K-H

Pfoto 16. Feb 2009 14:50

Re: globale Variablen
 
Zitat:

Zitat von Tharon
Delphi-Quellcode:
unit GlobalRessources;

...

TGlobalRessources = class
  private
    // ...
    constructor _Create; virtual;
  public
    constructor Create; virtual;
    class function RetrieveInstance(): TGlobalRessources;
    // ...
end;


implementation


var
  m_Instance:   TGlobalRessources;


constructor TGlobalRessources._Create;
begin
  inherited Create;
end;

[...]

Zu dem Code von Tharon hätte ich die Frage:

wie kann ich denn nun auf das einmalig instanzierte Objekte zugreifen?

Müsste "m_Instance" deshalb nicht _vor_ dem "implementation" Teil stehen oder gibt es da eine andere vorgehensweise?


Gruß
Jürgen

DeddyH 16. Feb 2009 15:01

Re: globale Variablen
 
Stimmt, die globale Variable muss in den interface-Teil, ansonsten ist sie nicht von außen erreichbar.

Hawkeye219 16. Feb 2009 15:03

Re: globale Variablen
 
Hallo,

der Zugriff erfolgt über die Klassenmethode RetrieveInstance, die eine Referenz auf die (einzige) Instanz liefert:

Delphi-Quellcode:
uses
  GlobalRessources;

begin
  TGlobalRessources.RetrieveInstance.<Eigenschaft>
end;
Gruß Hawkeye

sirius 16. Feb 2009 15:07

Re: globale Variablen
 
Zitat:

Zitat von DeddyH
Stimmt, die globale Variable muss in den interface-Teil, ansonsten ist sie nicht von außen erreichbar.

Eben nicht. Es darf ja auch keiner die Instanzvariable verändern.

Edit1: Im Finalization wäre ein einfaches nilfree besser.
Edit2: Habe ich im Edit1 tatsächlich "nil" geschrieben, ich meinte natürlich free.

DeddyH 16. Feb 2009 15:08

Re: globale Variablen
 
Man sollte auch den ganzen Ursprungscode lesen :oops:

Pfoto 16. Feb 2009 16:50

Re: globale Variablen
 
Zitat:

Zitat von Hawkeye219
Hallo,

der Zugriff erfolgt über die Klassenmethode RetrieveInstance, die eine Referenz auf die (einzige) Instanz liefert:

Delphi-Quellcode:
uses
  GlobalRessources;

begin
  TGlobalRessources.RetrieveInstance.<Eigenschaft>
end;
Gruß Hawkeye

ich verstehe immer noch nicht den echten Vorteil, als wenn
ich direkt über die im interface zugängliche Instanz auf das
globale Objekt zugreife...

Ist es, damit man das globale Objekte nicht aus Versehen freigibt?

Würde -- wenn ich unbedingt Dummheiten machen will -- nicht
auch folgendes funktionieren?

Delphi-Quellcode:
  TGlobalRessources.RetrieveInstance.Free;

Gruß
Jürgen

squetk 16. Feb 2009 17:04

Re: globale Variablen
 
Ich denke, man sollte hier kein Dogma setzen.

Schreibe ich allein ein kleines Tool, muss ich nicht diese Sicherheiten einbauen. Da bediene ich mich hemmungslos globaler Variablen.

Handelt es sich um eine große Anwendung, an der mehrere Entwickler arbeiten, ist die Vorgehensweise der Kapselung jedoch sinnvoll.

Hawkeye219 16. Feb 2009 18:12

Re: globale Variablen
 
Hallo Jürgen,

Zitat:

Zitat von Pfoto
Würde -- wenn ich unbedingt Dummheiten machen will -- nicht
auch folgendes funktionieren?

Delphi-Quellcode:
  TGlobalRessources.RetrieveInstance.Free;

Wenn du eine Dummheit begehen möchtest, kann dich niemand davon abhalten - schon gar nicht Delphi.

Das singleton pattern bietet durchaus einige Vorteile. Neben den bereits erwähnten (Schutz vor mehrfacher Instanziierung, Schutz vor versehentlichem Überschreiben der globalen Variablen) möchte ich noch einen weiteren nennen: Verzicht auf unnötige Instanziierung. Die globalen Objekte werden erst dann erzeugt, wenn sie wirklich benötigt werden. Das ist insbesondere dann sinnvoll, wenn das Erstellen der Objekte sehr zeitaufwendig ist oder sie viel Speicherplatz benötigen.

Beispiel für Singletons findest du übrigens auch in der VCL. Schaue dir mal den Quelltext der Units ClipBrd und Printers an.

Gruß Hawkeye

Pfoto 16. Feb 2009 18:54

Re: globale Variablen
 
Danke Hawkeye, jetzt ist es verständlich geworden. Gruß Jürgen


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:52 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