Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Variablen Unitübergreifend übergeben


mexx - Mo 29.01.07 16:53
Titel: Variablen Unitübergreifend übergeben
Ich rufe aus einer Unit eine Function aus. Diese Function liefert mir per Result ein Ergebnis zurück. Nun möchte ich aber mehr als nur ein Ergebnis erhalten. Ich könnte in der Unit2, wo sich die aufgerufene Function befindet weitere Variablen definieren und mit Werten belegen, aber wie kann ich diese Variablen in der Unit1 benutzen. Ich bin mir sicher, dass es geht und dass es einfach ist, aber ich weis nicht wie.

Ein Beispiel:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
Unit1
...
uses Unit2
...
erg := function math(1,1);
Showmessage(VarFuerUnit1); //Wie kann ich die Variable VarFuerUnit1, welche in der Unit2 belegt wurde in Unit1 nutzen.



Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
Unit2
...
uses Unit1
...
function math(zahl1,zahl2:integer):Integer;
begin
 result := zahl1 + zahl2;
 VarFuerUnit1 := 'Hallo Welt';
end;


Welches ist der sauberste und richtige Weg?


wdbee - Mo 29.01.07 17:08

Da gibt es je nach Einsatzzweck eine ganz Reihe von "richtigen" Lösungen.
Was aber generell möglich ist, egal ob du mit Klassen arbeiten willst oder nicht, ist der Einsatz einer Unit "Global" , in der du alle globalen Vereinbarungen im Interface-Abschnitt unterbringst (Type, Konstanten, Variablen usw.). Alle deine eigentlichen Units können die in der uses Klausel des eigenen Interfaces haben und so auch im Interface alles verwenden, ohne das es jemals Kreuzbezüge geben kann.

Im Quellcode schreibst du dann statt Variablenname einfach Global.Variablenname und das war es schon.


mexx - Mo 29.01.07 17:11

Demnach müsste es gehen, wenn ich globale Variablen in der Unit2 definiere. Dann müsste ich sie in der Unit1 benutzen können?


mexx - Mo 29.01.07 17:18

Thx! Da ja bekanntlich mehrere Wege nach Rom führen, hätte ich gedacht, es gibt eine Methode dafür, die von den Profis bevorzugt verwendet wird.


wdbee - Mo 29.01.07 17:32

Mag sein dass das bei dir geht, aber generell können dann Probleme auftreten, wenn du überkreuzende Bezüge zwischen deinen Units bekommst.


jaenicke - Mo 29.01.07 18:22

Wie wärs so:

Delphi-Quelltext
1:
2:
3:
4:
5:
function Division(zahl1, zahl2: Integer; var Rest: Integer): Integer; 
begin 
  Result := zahl1 div zahl2; 
  Rest := zahl1 mod zahl2;
end;


mexx - Di 30.01.07 09:19

user profile iconjaenicke hat folgendes geschrieben:
Wie wärs so:

Delphi-Quelltext
1:
2:
3:
4:
5:
function Division(zahl1, zahl2: Integer; var Rest: Integer): Integer; 
begin 
  Result := zahl1 div zahl2; 
  Rest := zahl1 mod zahl2;
end;


Wie kann ich var Rest verstehen? Als Eingabeparameter beim Aufruf der funktion oder als Ergebnismenge der Funktion?


Kroko - Di 30.01.07 10:20

nein, als Rückgabeparameter
daher würde ich den Kopf wie folgt abändern:

Delphi-Quelltext
1:
2:
3:
4:
5:
function Division(const zahl1, zahl2: Integer; out Rest: Integer): Integer; 
begin 
  Result := zahl1 div zahl2; 
  Rest := zahl1 mod zahl2;
end;


mexx - Mi 31.01.07 10:05

Rufe ich aber nun die Function auf, so erwartet er von mir einen weiteren Parameter Rest. Ich will den Parameter aber nicht beim Aufruf eingeben, sondern als weiteres Ergebnis erhalten.


nivosta - Mi 31.01.07 10:26

Guten morgen,

ich glaube der richtige Weg in diesem Fall ist wenn man eine Result Record strucktur deklariert und diese zurückgibt, ist auch Programmiertechnisch die sauberste Lösung


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
type
  TMeinResult = packed record
     var1 : string;
     var2 : integer;
  end;

.
.
.

function tuWas() : TMeinResult;
begin
     result.var1 := 'Test';
     result.var2 := 12;
end:


jaenicke - Mi 31.01.07 10:37

user profile iconmexx hat folgendes geschrieben:
Rufe ich aber nun die Function auf, so erwartet er von mir einen weiteren Parameter Rest. Ich will den Parameter aber nicht beim Aufruf eingeben, sondern als weiteres Ergebnis erhalten.

Genau das ist doch auch, was passiert!

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
function Division(const zahl1, zahl2: Integer; out Rest: Integer): Integer;   
begin   
  Result := zahl1 div zahl2;   
  Rest := zahl1 mod zahl2;  
end

var
  rest: Integer;
begin
  ShowMessage('12 durch 5 ist ' + IntToStr(Division(125, rest)) + ', Rest ' + IntToStr(rest));
end;


user profile iconnivosta hat folgendes geschrieben:
ich glaube der richtige Weg in diesem Fall ist wenn man eine Result Record strucktur deklariert und diese zurückgibt, ist auch Programmiertechnisch die sauberste Lösung

Ob das nun die sauberste Lösung ist, da kann man sich drüber streiten. Out existiert ja nicht ohne Grund und manchmal möchte ich nicht mit Records arbeiten, aber das ist Geschmackssache.
Es ist jedenfalls eine weitere Lösung, die natürlich auch gut funktioniert.


mexx - Mi 31.01.07 13:06

Ich finde die Lösung aber ganz gut. Bei mir stimmt nur was nicht. Schaut mal bitte.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
...
  public
    { Public-Deklarationen }
  end;

type
  TSaveResults = Record
   SSaveToFile, SSaveToTemplate: Boolean;
   SFileName, SSaveName: String;
  end;

  function  ReportSaveDialog(ADataSet: TDataSet): TSaveResults;

var
  FstReportDlg: TFstReportDlg;

implementation
{$R *.dfm}
...
function ReportSaveDialog(ADataSet: TDataSet): TSaveResults;
begin
...
 SSaveToFile := true; 
...
end;


[Fehler] FastRptDialg.pas(176): Undefinierter Bezeichner: 'SSaveToFile'

Woran liegts? Falsche Definietion des Records?


jaenicke - Mi 31.01.07 13:07

Es fehlt das Result. davor, denn das ist ja schließlich in dem Ergebnis-Record drin.


mexx - Mi 31.01.07 13:57

Aha! Das eigentliche Problem war aber, dass ich diese Ergebnisse, die nun in meinen Record stehen, auch in einer anderen Unit sehe. Dort ist aber derRecord, so wie der Quelltext jetzt ist, nicht zu sehen.


jaenicke - Mi 31.01.07 13:59

Dann erstelle eine neue Unit, pack dort die Record-Definition rein, und binde diese Unit via uses in alle Units ein, die den benutzen.


mexx - Mi 31.01.07 14:02

Der Aufruf der Werte geschieht über


Delphi-Quelltext
1:
ReportSaveDialog(Query).SSaveName                    


zum Beispiel. Aber rufe ich da nicht jedesmal die Funktion auf, wenn ich die Var SSaveName oder die anderen Records verwenden will?


jaenicke - Mi 31.01.07 14:04

Du solltest den Rückgabewert zunächst in eine Variable vom Typ des Records speichern ;-).


Kroko - Mi 31.01.07 14:05

user profile iconmexx hat folgendes geschrieben:
Rufe ich aber nun die Function auf, so erwartet er von mir einen weiteren Parameter Rest. Ich will den Parameter aber nicht beim Aufruf eingeben, sondern als weiteres Ergebnis erhalten.

(a)Die Proc erwartet keinen weiteren Parameter sondern eine Variable, die zurück gegeben werden soll!

PS: Gilt hier nicht mehr, neue Frage neuer Thread :?:


jaenicke - Mi 31.01.07 14:06

user profile iconKroko hat folgendes geschrieben:
PS: Gilt hier nicht mehr, neue Frage neuer Thread :?:

Ist doch immer noch dasselbe Problem... :gruebel:


mexx - Mi 31.01.07 14:07

user profile iconjaenicke hat folgendes geschrieben:
Du solltest den Rückgabewert zunächst in eine Variable vom Typ des Records speichern ;-).


Dann erachte ich diese Lösung als eher unangenehm und nicht so praktisch.


Kroko - Mi 31.01.07 15:07

user profile iconjaenicke hat folgendes geschrieben:
user profile iconKroko hat folgendes geschrieben:
PS: Gilt hier nicht mehr, neue Frage neuer Thread :?:

Ist doch immer noch dasselbe Problem... :gruebel:

:gruebel: function Division...
:gruebel: =ReportSaveDialog(Query).SSaveName
:gruebel:


:wink:


jaenicke - Mi 31.01.07 19:13

user profile iconKroko hat folgendes geschrieben:
user profile iconjaenicke hat folgendes geschrieben:
user profile iconKroko hat folgendes geschrieben:
PS: Gilt hier nicht mehr, neue Frage neuer Thread :?:

Ist doch immer noch dasselbe Problem... :gruebel:

:gruebel: function Division...
:gruebel: =ReportSaveDialog(Query).SSaveName
:gruebel:

:lol: Meine Funktion Division war doch nur ein Beispiel, danach war doch gar nicht gefragt! Dass die Funktion nun ReportSaveDialog heißt, um die es geht, ist doch egal...

user profile iconmexx hat folgendes geschrieben:
user profile iconjaenicke hat folgendes geschrieben:
Du solltest den Rückgabewert zunächst in eine Variable vom Typ des Records speichern ;-).


Dann erachte ich diese Lösung als eher unangenehm und nicht so praktisch.

Ähm, wie willst du denn mit zwei Rückgabewerten gleichzeitig arbeiten ohne zumindest einen davon zwischenzuspeichern??? :gruebel:

Und an meinem Beispiel siehst du, wie du innerhalb einer "Abfrage" mit dem zweiten Ergebnis gleich weiterarbeiten kannst (dann mit dem übergebenen Speicherort für den zweiten Rückgabewert als out-Parameter).

Ohne eine Variable UND ohne einen Neuaufruf der Funktion ist es IMHO vollkommen unmöglich! Und zwar nicht nur in Delphi...

Wenn du ohne Variable auskommen musst/willst, dann ist dein Konzept ähh... sagen wir... IMHO nicht sinnvoll.
Ich sehe auch keinen Grund, warum man das irgendwo ohne Variable machen müsste. Wirklich! Das meine ich ernst! Ich sehe keinen Fall, wo das nur ohne Variable ginge/sinnvoll wäre....

Wenn das bei dir nicht geht, dann solltest du vielleicht mal sagen, warum. Vor allem, weil du doch sogar globale Variablen als Möglichkeit in Betracht gezogen hast.
Mit denen kannst du es durchaus machen. Würde ich zwar nicht so machen, aber es geht damit.

BTW: Wenn du die Windows API verwendest, dann kommst du um Variablen als Parameter, in die der Rückgabewert reinkommt, nicht herum. (Ob man die API jetzt in Zeiten der OOP als Beispiel nehmen sollte, sei mal dahingestellt... ;-))