Autor Beitrag
IhopeonlyReader
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 600
Erhaltene Danke: 23


Delphi 7 PE
BeitragVerfasst: Di 02.04.13 15:23 
Guten Tag,
ich versuche gerade eine Procedure zur Laufzeit zu ändern.. vorerst ist dies auch möglich.. Beispiel

ausblenden 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:
type
   EinBeispiel=class(TObject)
   Auszufuehren: Boolean;
   OnAusfuehren: procedure;
end;

var eT: EinBeipsiel;

//vorherige proceduren
prcedure Test1;
begin
showmessage('Hi');
end;

procedure Test2;
begin
showmessage('toll');
end;

//Zuweisung erfolgt:
eT.OnAusfuehren := Test1; //klappt (wobei man natürlich in diesem Simplen Beispiel einfach den string abspeichern könnte..., es geht am um das Prinzip der Zuweisung

//Aufruf
eT.OnAusfueren;


Das klappt alles einwandfrei... jedoch würde ich das gerne so gestalten
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
//Klasse EinBeispiel ist gleich, variable eT ebenfalls..

//vorherige Proceduren
procedure GibAus(Text: String; UnNochwas: Boolean);
begin
//was weiß ich was gemacht werden soll
end;

//Zuweisung soll:
eT.OnAusfuehren := GibAus(Edit1.Text, Checkbox1.Checked);

//aufruf wie vorher

... es wird vom Compiler die Fehlermeldung ausgegeben:
"inkompatible typen 'Procedure' und 'procedure'"
was mich persönlich verwirrt...
da es viele Möglichkeiten gibt, und ich nicht alle abspeichern will (alle möglichen variablen in dyn. Arrays abspeichern, proceduren nummern zuweisen und auf abruf entsprechend einsetzen)

ist sowetwas möglich, wie ich es vorhabe?

_________________
Sucht "neueres" Delphi :D
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
Perlsau
Ehemaliges Mitglied
Erhaltene Danke: 1



BeitragVerfasst: Di 02.04.13 15:50 
user profile iconIhopeonlyReader hat folgendes geschrieben Zum zitierten Posting springen:
Guten Tag, ich versuche gerade eine Procedure zur Laufzeit zu ändern.

Und wozu soll das gut sein?
IhopeonlyReader Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 600
Erhaltene Danke: 23


Delphi 7 PE
BeitragVerfasst: Di 02.04.13 15:59 
user profile iconPerlsau hat folgendes geschrieben Zum zitierten Posting springen:
Und wozu soll das gut sein?


Um allgemin gültige Typen zu schreiben :D
z.B. für Mapeditoren, wobei es dann Prozeduren wie
Feldbetreten
Feldverlassen
gäbe...
da es verschiedene Arten von "Feldern" gibt, und alle dieselben proceduren besitzen, fände ich es sehr umständlich ein Typ mit abstrakten Methoden zu schreiben, da sich ein anderer Typ auf den Typ Feld bezieht...

Beispiel
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
Type TMap=class
Felder: Array of Array of TFeld;
procedure zeichnen;
end;

//Diese Map besitzt nun Felder, die vom Typ TFeld sind..

type TFeld=class
Feldbetreten: procedure;
Feldverlassen: procedure;
zeichnen: procedure;
end;

//die procedure zeichnen der klasse TMap ist immer gleich, nämlich:
For C:=0 to High(Felder) do
For C1:=0 to High(Felder[C] do
  Felder[C,C1].zeichnen;


die procedure zeichnen vom Typ TFeld könnte z.B. sein: ZeichneGras(clgreen); oder zeichneHausTeil(A1); oder auch nur procedure leer; (ohne Parameter)

das Problem ist, es ist immer die selbe "standart prozedure mit FESTEN Parametern", ich könnte natürlich bei nur 3 proceduren auch 3 variablen anlegen
einmal: eineFarbe: TColor;
einHausTyp: THaus;
procedureNR: Integer;

dann müsste ich für die erste procedure (zeichneGras)
eineFarbe auf clgreen setzen und procedureNR den Wert 1 zuweisen...

auf abruf würde dann folgendes passieren:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure zeichnen:
begin
case procedureNr of
1: ZeichneGras(eineFarbe);
2: ZeichneHausTeil(einHausTyp)
3: leer;
else
end;
end;


bei vielen Proceduren mit vielen verschiedenen Parametern sehr umständlich

wenn mann das ganze jetzt in viel größeren Stile machen würde, wäre es sehr umständlich z.B. verschiedene Feldarten (gras, haus...) in einzelne typen zu legen...
Abstract gestalten ist hier ebenfalls wergen der unterschiedlichen Parameter nicht möglich

_________________
Sucht "neueres" Delphi :D
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Di 02.04.13 16:32 
Tja, da würde ich empfehlen: In TFeld richtige Methoden als virtual deklarieren. In einem TGrasFeld ableiten und per override implementieren. ;-)
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8548
Erhaltene Danke: 477

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Di 02.04.13 16:52 
Auch wenn die Möglichkeiten der Objektorientierten Programmierung vermutlich sinnvoller sind: Für sowas muss man sich einen eigenen Typ definieren:

ausblenden Delphi-Quelltext
1:
2:
3:
TMeineProzedurenMitSpeziellenParameter = procedure(aParam: String; bParam: Integer; cParam: Boolean);
//oder, für Klassenmethoden
TMeineProzedurenMitSpeziellenParameter = procedure(aParam: String; bParam: Integer; cParam: Boolean) of Object;

_________________
We are, we were and will not be.
IhopeonlyReader Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 600
Erhaltene Danke: 23


Delphi 7 PE
BeitragVerfasst: Di 02.04.13 16:59 
user profile iconjfheins hat folgendes geschrieben Zum zitierten Posting springen:
Tja, da würde ich empfehlen: In TFeld richtige Methoden als virtual deklarieren. In einem TGrasFeld ableiten und per override implementieren. ;-)

Das Problem: dann ist, wenn man ein Object vom Typ TMap erstellt und dann Map.zeichnen aufruft, leider das ganze Schiefläuft, da die Klasse TMap auf das allgemeine TFeld zurückgreift...


user profile iconGausi hat folgendes geschrieben Zum zitierten Posting springen:
Auch wenn die Möglichkeiten der Objektorientierten Programmierung vermutlich sinnvoller sind: Für sowas muss man sich einen eigenen Typ definieren:

ausblenden Delphi-Quelltext
1:
2:
3:
TMeineProzedurenMitSpeziellenParameter = procedure(aParam: String; bParam: Integer; cParam: Boolean);
//oder, für Klassenmethoden
TMeineProzedurenMitSpeziellenParameter = procedure(aParam: String; bParam: Integer; cParam: Boolean) of Object;

nur was bringt mir das? soll ich dann ganz viele überladene proceduren machen?
denn
type TFeld=class
zeichnen: TMeineProzedurenMitSpeziellenParameter;
zeichnen: TMeineProzedurenMitSpeziellenParameter1; overload;
...
denn sonst kann ich ja der prozedure zeichnen keine "verschieden parameterhaltige" proceduren zuweisen

Edit: die Parameter von zeichnen sind ja auch konstant ! daher brauch ich auch eigentlich kein parameterhaltigen Prozeduren;

_________________
Sucht "neueres" Delphi :D
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
jfheins
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 918
Erhaltene Danke: 158

Win 10
VS 2013, VS2015
BeitragVerfasst: Di 02.04.13 17:47 
user profile iconIhopeonlyReader hat folgendes geschrieben Zum zitierten Posting springen:
Das Problem: dann ist, wenn man ein Object vom Typ TMap erstellt und dann Map.zeichnen aufruft, leider das ganze Schiefläuft, da die Klasse TMap auf das allgemeine TFeld zurückgreift...

hast du dich schonmal intensiver mit Vererbung auseinander gesetzt? Wenn die Methoden korrekt überschrieben sind und es sich tatsächlich um ein TGrasFeld handelt (um beim Beispiel zu bleiben) wird auch von TMap die korrekte Methode TGrasFeld.zeichnen() aufgerufen.
IhopeonlyReader Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 600
Erhaltene Danke: 23


Delphi 7 PE
BeitragVerfasst: Di 02.04.13 18:32 
wenn ich also
var Map: TMap;
deklariere und ich dann
Map := TMap.Create; und dann
Map.Feld[C,C1] := TGrasFeld.Create;
dann ist Map.Feld richtig, allerdings ! wollte ich eigentlich bei TMaps im Construktor folgendes aufrufen:
Feld[C,C1] := TFeld.Create;
(C und C1 soll heißen For C:=0 to High(Feld) do C1:=0 to High(Feld[C] do )

also heißt das, dass ich die Felder nicht in der Klasse TMap erstellen kann ?!, da TMap ja nicht TGrasFeld kennt, sondern nur TFeld

_________________
Sucht "neueres" Delphi :D
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
Quitzlinga
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 60
Erhaltene Danke: 2

Win XP
Delphi 2007 Prof. Codegear Win32
BeitragVerfasst: Mi 03.04.13 09:16 
Hi,

ich kann mich den Antworten nur anschliessen. Dein Problem ist ein Paradebeispiel für Vererbung und Objektorientierung.
Da man aus Beispielen aber im allgemeinen mehr lernt, habe ich mal versucht, es abzubilden

ausblenden volle Höhe 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:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
unit uMapVerwaltung;

interface

uses Graphics;

type TFeldType = (ftGras, ftSteppe, ftWasser);

type TFeld = class(TObject)
  private
    procedure Zeichnen(x, y : integer; aCanvas : TCanvas);virtual;abstract;
  public
    procedure Betreten();virtual;abstract;
    procedure Verlassen();virtual;abstract;
end;

type TGrasFeld = class(TFeld)
  private
    procedure Zeichnen(x, y : integer; aCanvas : TCanvas);override;
  public
    procedure Betreten();override;
    procedure Verlassen();override;
end;

type TSteppeFeld = class(TGrasFeld)
  private
    procedure Zeichnen(x, y : integer; aCanvas : TCanvas);override;
  public
    procedure Betreten();override;
    procedure Verlassen();override;
end;

type TWasserFeld = class(TFeld)
  private
    procedure Zeichnen(x, y : integer; aCanvas : TCanvas);override;
  public
    procedure Betreten();override;
    procedure Verlassen();override;
end;

type TMapArray = Array of Array of TFeld;   // Ein dynamisches 2Dim Array

type TMap = class(TObject)
  private
    fMap : TMapArray;
    fLinkCanvas : TCanvas;
    procedure Init(x, y : integer);
    procedure Setze(x, y : integer; aFeldType : TFeldType);
  public
    constructor Create();
    destructor Destroy();
    procedure Betrete(x, y : integer);
    procedure Verlasse(x, y : integer);
    procedure Zeichne();overload;
    procedure Zeichne(x,y : integer);overload;
    property Canvas : TCanvas read fLinkCanvas write fLinkCanvas;
end;

implementation

uses SysUtils, Dialogs;

//******************************** TGrasFeld ***********************************
procedure TGrasFeld.Zeichnen(x, y : integer; aCanvas : TCanvas);
begin
  ShowMessage('Zeichne Gras');
end;

procedure TGrasFeld.Betreten();
begin
  ShowMessage('Betrete Gras');
end;

procedure TGrasFeld.Verlassen();
begin
  ShowMessage('Verlasse Gras');
end;
//******************************** TSteppeFeld *********************************
// Steppe ist Gras mit Steinen
procedure TSteppeFeld.Zeichnen(x, y : integer; aCanvas : TCanvas);
begin
  inherited Zeichnen(x, y, aCanvas); // Rufe Grasfeldzeichnen auf
  ShowMessage('Zeichne zusätzlich Steine');
end;

procedure TSteppeFeld.Betreten();
begin
  inherited Betreten(); // Betreten ist wie bei Gras
end;

procedure TSteppeFeld.Verlassen();
begin
  inherited Verlassen(); // Verlassen ist wie bei Gras
end;
//******************************** TWasserFeld *********************************
procedure TWasserFeld.Zeichnen(x, y : integer; aCanvas : TCanvas);
begin
  ShowMessage('Zeichne Wasser');
end;

procedure TWasserFeld.Betreten();
begin
  ShowMessage('Betrete Wasser');
end;

procedure TWasserFeld.Verlassen();
begin
  ShowMessage('Verlasse Wasser');
end;
//******************************** TMap ****************************************
constructor TMap.Create();
begin
  Init(20,20);
end;

destructor TMap.Destroy();
var
  i, j : integer;
begin
  for i := Low(fMap) to High(fMap) do begin
    for j := Low(fMap[i]) to High(fMap[i]) do begin
      FreeAndNil(fMap[i,j]);
    end;
  end;
end;

procedure TMap.Init(x, y : integer);
var
  i,j : integer;
begin
  SetLength(fMap, x);
  for i := Low(fMap) to High(fMap) do SetLength(fMap[i], y);

  for i := Low(fMap) to High(fMap) do begin
    for j := Low(fMap[i]) to High(fMap[i]) do begin
      fMap[i,j] := NIL;
    end;
  end;

end;

procedure TMap.Setze(x, y : integer; aFeldType : TFeldType);
begin
  FreeAndNil(fMap[x,y]);
  case aFeldType of
    ftGras   : fMap[x,y] := TGrasFeld.Create();
    ftSteppe : fMap[x,y] := TSteppeFeld.Create();
    ftWasser : fMap[x,y] := TWasserFeld.Create();
  end;
  Zeichne(x, y);
end;

procedure TMap.Zeichne();
var
  i,j : integer;
begin
  // Zeichne alles
  for i := Low(fMap) to High(fMap) do begin
    for j := Low(fMap[i]) to High(fMap[i]) do begin
      // Individuelle Zeichenroutine des Feldes
      fMap[i,j].Zeichnen(i, j, fLinkCanvas);
    end;
  end;
end;

procedure TMap.Zeichne(x, y : integer);
begin
  fMap[y,x].Zeichnen(x, y, fLinkCanvas);
end;

procedure TMap.Betrete(x, y : integer);
begin
  // Rufe die individuelle Betretenroutine des Feldes auf
  if fMap[x,y] <> NIL then fMap[x,y].Betreten();
end;

procedure TMap.Verlasse(x, y : integer);
begin
  // Rufe die individuelle Verlassenroutine des Feldes auf
  if fMap[x,y] <> NIL then fMap[x,y].Verlassen();
end;

end.


Das ganze müsste den Sachverhalt von Dir recht gut abbilden. Wie Du sehen kannst, kannst Du auch ohne Änderungen zur Laufzeit individuelle Aufrufe pro Objekt machen. Individuelle Anpassungen der Routinen zur Laufzeit sind ohnehin keine gute Idee, da Du irgentwann den Überblick verlierst und das Debugging in einen solchen Fall sehr schwer ist.

Das ganze ist ungetestet aber vom Grundsatz müsste es stimmen.

MfG

Quitzlinga
IhopeonlyReader Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 600
Erhaltene Danke: 23


Delphi 7 PE
BeitragVerfasst: Mi 03.04.13 13:23 
ich kenne solche Vererbung klar, aber ich habe eigentlich versucht diese zu vermeiden, da ich verschiedene Parameter immer habe, gras zeichnen muss ich nur grasfarbe angeben, bei steppe gras und steinfarbe als Beispiel...
dann müsste ich alle unterschiede ins create Ereignis packen oder?

_________________
Sucht "neueres" Delphi :D
Wer nicht brauch was er hat, brauch auch nicht was er nicht hat!
Gausi
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 8548
Erhaltene Danke: 477

Windows 7, Windows 10
D7 PE, Delphi XE3 Prof, Delphi 10.3 CE
BeitragVerfasst: Mi 03.04.13 13:55 
Dann nimm die Parameter aus der Zeichnen-Funktion raus und nutze dafür Felder der Klasse, die nach dem Create entsprechend gesetzt werden. Wenn ein Feld erstmal vorhanden ist, dann ändert sich das Feld ja nicht ständig, so dass man der zeichnen-Funktion ständig diese Parameter mitgeben muss, oder?

_________________
We are, we were and will not be.