Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Variable, die in einer Unit deklariert nutzen


luxoar - So 18.01.09 13:43
Titel: Variable, die in einer Unit deklariert nutzen
Guten Tag,
ich habe mal wieder ein Problem. Ich möchte eine Variable einer Prozedur aus einer zweiten Unit in ein Label der ersten Unit(mit Form1) übergeben. Allerdings zeigt er die Variable in der ersten unit als nich deklariert an. Ich suche den Fehler leider bisher vergeblich :(. Könntet ihr mir bei diesem kleinen Problem helfen? Danke schon einmal im Vorraus :)


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:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
unit u_bedienung;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, u_bankomat, StdCtrls;

type
  tform1 = class(TForm)
    Label1: TLabel;
    Label2: TLabel;
    Edit1: TEdit;
    Button1: TButton;
    Button2: TButton;
    GroupBox1: TGroupBox;
    ListBox1: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  form1: tform1;
  banko: Bankomat;
implementation

{$R *.dfm}

procedure tform1.Button1Click(Sender: TObject);
begin
Bankomat.getkontostand(stand);  [b]--> [DCC Fehler] u_bedienung.pas(35): E2003 Undefinierter Bezeichner: 'stand'[/b]
label1.caption:=floattostr(stand);
end;

procedure tform1.FormCreate(Sender: TObject);
begin
banko:=Bankomat.create;
end;

end.

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:
26:
27:
28:
unit u_Bankomat;

interface

type
Bankomat = class (tobject)
public
constructor create;
procedure getkontostand(var stand:single);

end;


implementation

constructor Bankomat.create;
begin
inherited create;
end;

procedure Bankomat.getkontostand(var stand:single);
begin
randomize;
stand:=random(200)+100;
end;


end.


Moderiert von user profile iconNarses: Delphi-Tags hinzugefügt


Delete - So 18.01.09 13:51

Du hast die Variable stand ja auch nirgends deklariert. Versuch es mal so:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
procedure tform1.Button1Click(Sender: TObject);
var stand: single;
begin
  Banko.getkontostand(stand); 
  label1.caption:=floattostr(stand);
end;


Yogu - So 18.01.09 14:04

Hallo,

bitte benutze für deinen Quelltext die Delphi-Tags <span class="inlineSyntax"><span class="codecomment">{PROTECTTAGd9b754d46fc1a62d5e1533823def0247}</span></span>, damit wir ihn besser lesen können. Klicke dazu in deinem Beitrag einfach auf user defined image, und füge die Tags hinzu. Danke!

Zu deiner Frage: Du kannst nicht einfach auf Parameter (so heißen die Variablen, die Prozeduren und Funktionen übergeben wurden), von außerhalb zugreifen. Aber das ist in deinem Fall auch gar nicht notwendig. Die Lösung ist eine lokale Variable in der Prozedur Button1Click, wie user profile iconDeddyH schon schrieb.

Grüße,
Yogu


luxoar - So 18.01.09 15:05

Ahhh vielen dank. Ich hatte diese Quelltext-Bearbeitungsmethode schon gesucht ich merke es mir. Und natürlich vielen Dank für die schnelle hilfreiche antwort. Das mit der Deklaration der Variable in Button_click1 hatte ich dann auch gesehen. Allerdings bin ich nicht auf die Zeile: Banko.getkontostand(stand); gekommen. Noch einmal vielen dank :)


jaenicke - So 18.01.09 15:13

Du machst mehrere Fehler.

Delphi-Quelltext
1:
Bankomat.getkontostand                    
Bankomat ist dein Typ, nicht dein erzeugtes Objekt. Banko musst du benutzen.
Nenne Typen am besten wie es Konvention ist mit einem T am Anfang: TBankomat.

Du vergisst dein Objekt wieder freizugeben! In FormDestroy gehört Banko.Free; !

Randomize gehört nur einmal aufgerufen, nicht jedesmal vor Random. Es initialisiert den Zufallsgenerator, und das sollte nur einmal passieren.

Alles klein schreiben ist auch keine gute Idee, bei zusammengesetzten Befehlen weiß keiner was gemeint ist.

Und wenn du einen Wert zurückbekommen willst, dann kannst du auch eine Funktion verwenden. Die ist dafür gedacht.

Hier einmal mein Vorschlag: ;-)

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:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
unit u_bedienung;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, u_bankomat, StdCtrls;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Label2: TLabel;
    Edit1: TEdit;
    Button1: TButton;
    Button2: TButton;
    GroupBox1: TGroupBox;
    ListBox1: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject); // Das musst du natürlich auch bei OnDestroy eintragen
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
    Banko: TBankomat;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Label1.Caption := FloatToStr(Banko.GetKontostand());
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Randomize;
  Banko := TBankomat.Create;
end;

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

end.

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:
26:
27:
unit u_Bankomat;

interface

type
  TBankomat = class(TObject)
  public
    // constructor Create;
    function GetKontostand: Single;
  end;


implementation

// Wenn der Konstruktor leer ist, wird er auch erst einmal nicht gebraucht
//constructor TBankomat.Create;
//begin
//  inherited Create;
//end;

function TBankomat.GetKontostand: Single;
begin
  // Randomize; // Das sollte nur einmal in FormCreate z.B. stehen
  Result := Random(200) + 100;
end;

end.


luxoar - So 18.01.09 17:02

Wow das macht ja richtig spaß hier etwas zu fragen. Mit so viel tollen Tipps hätte ich gar nicht gerechnet. Wirklich vielen vielen Dank dafür. :)


Delete - So 18.01.09 17:35

Wobei die meisten von user profile iconjaenicke angesprochenen "Fehler" eher Stilfragen sind :mrgreen:.


luxoar - So 18.01.09 17:52
Titel: Das ganze anders herum
So danke euch bin ich schon einen Schritt weiter.

Allerdings bekomme ich nun den umgekehrten Weg auch wieder nicht hin. :(
Ich möchte den Betrag aus edit1.text gerne in der u_bankomat benutzen. Allerdings bekomme ich den Wert nicht darüber. Wie kann man das am gescheitesten machen? Vielen dank schon einmal im Vorraus.


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:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
unit u_bedienung;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, u_bankomat, StdCtrls;

type
  tform1 = class(TForm)
    Label1: TLabel;
    Label2: TLabel;
    Edit1: TEdit;
    Button1: TButton;
    Button2: TButton;
    GroupBox1: TGroupBox;
    ListBox1: TListBox;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure einlesen(betrag:integer);
    procedure FormDestroy(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  form1: tform1;
  Bankomat: TBankomat;
implementation

{$R *.dfm}

procedure tform1.Button1Click(Sender: TObject);
var stand:single;
begin
Bankomat.getkontostand(stand);
label1.caption:=floattostr(stand);
end;

procedure tform1.Button2Click(Sender: TObject);
var ks:single;
    betrag:integer;
begin
einlesen(betrag);
Bankomat.konto_aendern(ks);
label2.caption:=floattostr(ks);
end;

procedure tform1.FormCreate(Sender: TObject);
begin
Bankomat:=TBankomat.create;
Randomize;
end;

procedure tform1.FormDestroy(Sender: TObject);
begin
Bankomat.free;
end;

procedure tform1.einlesen(betrag: Integer);
begin
  betrag:=strtoint(edit1.text); //--> den Betrag in u_bankomat in der Prozedur bankomat.konto_aendern(ks) verwenden
end;
end.


----------------------------------------------------------------------------------------------------------------------------

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:
26:
27:
28:
29:
30:
31:
32:
33:
34:
unit u_Bankomat;

interface

type
TBankomat = class (tobject)
public
procedure getkontostand(var stand:single);
function init:single;
procedure konto_aendern(var ks:single);
end;


implementation


function TBankomat.init:single;
begin
init:=random(200)+100;
end;


procedure TBankomat.getkontostand(var stand:single);
begin
stand:=init;
end;

procedure TBankomat.konto_aendern(var ks: Single);
var Betrag:integer;
begin
  ks:=Betrag; //hier kommt er nicht an
end;

end.


jaenicke - So 18.01.09 18:02

Überlege einmal was du machst: In einlesen weist du betrag einen Wert zu. Dieser ist aber kein variabler Wert, du änderst also nicht den Wert in Button2Click damit.
Es sollte auch eine entsprechende Warnung / Hinweis vom Compiler kommen, dass der an betrag zugewiesene Wert nie benutzt wird, oder?
Warum benutzt du denn keine Funktion? Schließlich willst du ja einen Wert zurückbekommen. ;-)

In konto_aendern dann überschreibst du den in ks übergebenene Wert mit dem (uninitialisierten) Wert der lokalen Variablen Betrag. Auch dort sollte eine Warnung kommen, nämlich dass Betrag nicht initialisiert wurde.

// EDIT:
Du müsstest den Kontostand schon irgendwo speichern. Im Moment benutzt du ja jedesmal einen anderen zufälligen Wert.

Delphi-Quelltext
1:
2:
3:
4:
5:
TBankomat = ...
private
  Kontostand: Single;
public
  ...


Delete - So 18.01.09 18:02

Der beste Weg (und auch im Sinne der OOP) für so etwas ist in meinen Augen die Verwendung von Properties mit entsprechenden Getter-/Settermethoden.
Beispiel:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
type TMyClass = class
  private
    FBetrag: single;
    function GetBetrag: single;
    procedure SetBetrag(const value: single);
  public
    property Betrag: single read GetBetrag write SetBetrag;
  end;
...
implementation

function TMyClass.GetBetrag: single;
begin
  //einfache Variante, könnte auch das Ergebnis einer Berechnung sein
  Result := FBetrag; 
end;

procedure TMyClass.SetBetrag(const value: single);
begin
  //wieder die einfache Variante, man könnte auch zunächst value abprüfen und ggf. verwerfen
  FBetrag := value;
end;

Verwendung:

Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TForm1.Button1Click(Sender: TObject);
begin
  if MyClass.Betrag < 6.5 then //Abfrage des Getters
    MyClass.Betrag := 10.5//Aufruf des Setters
end;


Yogu - So 18.01.09 20:50

user profile iconDeddyH: Get- und Set-Methoden braucht man nur, wenn mehr als eine einfache Wertzuweisung stattfinden soll. Ok, vielleicht muss die set-Prozedur noch eine Aktualisierungsmethode aufrufen, aber der Getter wird wohl nicht mehr machen müssen, als den Wert zurückzuliefern.


Delete - So 18.01.09 21:24

Die Kommentare hast Du aber gelesen, oder?


Yogu - So 18.01.09 22:23

user profile iconDeddyH hat folgendes geschrieben Zum zitierten Posting springen:
Die Kommentare hast Du aber gelesen, oder?

Ja, hab ich. Ich wollte nur noch ergänzen, dass Eigenschaften auch ohne Methoden möglich sind, da das in sehr vielen Fällen sinnvoller ist. Für Berechnungen etc. ist deine Variante natürlich unabdingbar.


Delete - So 18.01.09 22:26

Dann hatte ich Dich falsch verstanden. In meinem Minimalbeispielen könnte man natürlich über die Property direkt auf die private Variable zugreifen, das würde da keinen Unterschied machen, stimmt natürlich. Aber Getter- und Settermethoden sind durchaus sinnvoll einzusetzen, das wollte ich nur betonen.