Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Prozedur als Eigenschaft
FinnO - Sa 23.05.09 22:49
Titel: Prozedur als Eigenschaft
Hi Leute,
Ist es möglich, einem Objekt eine Prozedur als Eigenschaft zu verpassen? Ich meine dass in etwa so:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| type TFoo = class private public Action : Procedure;
end; |
Ziel soll es sein, dass ich dem Foo dann hinterher sowas zuweisen kann:
Delphi-Quelltext
1:
| Foo.Action := Button1Click(Sender); |
geht das? Und wenn ja, wie?
ffgorcky - So 24.05.09 08:20
Mir sieht das eher nach einer Funktion aus.
Also die wäre dann z.B. so in etwa:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| function AdditionsErgebnisAlsoSumme(Addiator1,Addiator2:int):int; begin AdditionsErgebnisAlsoSumme:=Addiator1+Addiator2; result:=Addiator1+Addiator2; end; |
Ich frage mich immer, welches sauberer ist - gehen tut doch beides -- oder?
Die beiden Parameter könntest Du Dir natürlich auch sparen, wenn Du stattdessen direkt auf Deine Objekt-Werte zugreifst.
Oder habe ich die Frage gerade total verkehrt verstanden?
FinnO - So 24.05.09 11:20
So ich habe das jetzt so zum laufen gebracht. Was mich jetzt ein bisschen stört ist, dass ich nur Methoden aufrufen kann, die genau die Parameter haben, wie ich in meinem Zeiger da festgelegt habe. Kann ich das auch so lösen, dass beliebige Prozeduren aufgerufen werden können?
Delete - So 24.05.09 11:50
Ganz so, wie Du das willst, geht es nicht, aber Du könntest Deine Methoden überladen (Stichwort overload).
DHE - So 24.05.09 17:28
Statt Methoden zu überladen kannst du auch einfach Interfaces nutzen.
Dann übergibst du der Methode als Parameter einfach eine vom Interface abgeleitete Klasse und
diese implementiert dann einfach die Funktion.
Yogu - So 24.05.09 19:28
FinnO hat folgendes geschrieben : |
| Was mich jetzt ein bisschen stört ist, dass ich nur Methoden aufrufen kann, die genau die Parameter haben, wie ich in meinem Zeiger da festgelegt habe. Kann ich das auch so lösen, dass beliebige Prozeduren aufgerufen werden können? |
Wenn du eine Prozedur mit Parametern aufrufst, musst du ja auch irgendwelche Parameter übergeben. Wo legst du diese Werte fest?
Eine Möglichkeit wäre eine Assembler-Anweisung. So habe ich externe Funktionen meiner Script-Eingine gelöst:
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:
| var ParameterCount, Result, Paramter1, Parameter2, Parameter3: Integer; Pntr: Pointer; begin case ParameterCount of 0: asm CALL Pntr MOV tmp1, EAX end;
1: begin asm MOV EAX, Parameter1 CALL Pntr MOV Result, EAX end; end;
2: begin asm MOV EAX, Parameter1 MOV EDX, Parameter2 CALL Pntr MOV Result, EAX end; end;
3: begin asm MOV EAX, Parameter1 MOV EDX, Parameter2 MOV ECX, Parameter3 CALL Pntr MOV Result, EAX end; end;
4: begin asm MOV EAX, Parameter1 MOV EDX, Parameter2 MOV ECX, Parameter3 PUSH Parameter4 CALL Pntr MOV Result, EAX end; end; end; |
ParameterCount gibt die Anzahl der Parameter an,
Pntr ist ein Zeiger auf die Funktion und in der Variable
Result ist nachher der Rückgabewert gespeichert. Allerdings musst du selbstverständlich vorher wissen, wie viele Parameter die Funktion hat, denn wie willst du sonst wissen, wie viele Werte auf den Stack geschrieben werden sollen (siehe
PUSH).
Ich weiß selbst, dass meine Case-Konstruktion nicht gerade das Optimum ist, und würde mich selbst um eine Verbesserung des Codes freuen. Das soll aber nicht Thema dieses Threads sein, falls
FinnO die Variante nicht will.
FinnO - So 24.05.09 19:44
also momentan habe ich folgendes Konstrukt:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| type TAction = procedure of object;
TFoo = class public Action : TAction; end; |
Zuweisung:
Delphi-Quelltext
1:
| Foo.Action := Form1.Close; |
Aufruf:
Mein Wunsch wäre jetzt ja eine Universallösung, in der ich JEDE beliebige Prozedur so zuweisen kann und später wieder aufrufen kann. Wenn das gar nicht klappen sollte, muss ich da wohl eine andere, unbequeme Lösung finden :(
€: Vielleicht noch zum Verwendungszweck: Ich möchte recht variabel einem Objekt eine Methode zuweisen. Diese Methode soll halbwegs zur Laufzeit zugewiesen werden - ich nehme aus einem Pool an Möglichen Funktionen eben die gewünschte. Da sollten so Dinge wie
Showmessage('Hallo Welt');genau so funktionieren wie eben
FormX.Close;
Yogu - So 24.05.09 20:01
FinnO hat folgendes geschrieben : |
| Da sollten so Dinge wie Showmessage('Hallo Welt');genau so funktionieren wie eben FormX.Close; |
Ist diese 'Hallo Welt' konstant, oder soll das tatsächlich als Parameter übergeben werden? Dein Code zum Aufruf lässt keine Parameter zu:
FinnO hat folgendes geschrieben : |
Aufruf:
|
Oder wo soll dda bitte schön das 'Hallo Welt' hin? Erklär doch mal genau, was du machen möchtest, oder gehe auf einzelne Vorschläge von uns ein. Ich denke, davon gibt es jetzt bereits genug.
FinnO - So 24.05.09 20:23
Nein die soll nicht als Parameter übergeben werden, sonder das Hallo Welt ist ja Parameter von Showmessage. Jetzt möchte ich erreichen, dass erstens:
Delphi-Quelltext
1:
| Foo.Action := Showmessage('Hallo Welt'); |
funktioniert und, dass ich das ganze dann mitt
Foo.Action auch wieder aufrufen kann.
aber genau so auch noch Foo.Action := Form1.Close oder Foo.Action := Button1OnClick(Sender); Halt etwas beliebiges stehen kann. Einfach eine Prozedur oder Funktion als Eigenschaft in einer Klasse.
Yogu - So 24.05.09 21:17
Ah, jetzt verstehe ich. Das ist gar nicht mal so schwer, wie du es dir vorstellst. Du musst nur ein wenig "umdenken":
Erstelle einfach eine kleine Funktion, die den Aufruf beinhaltet:
Delphi-Quelltext
1: 2: 3: 4:
| procedure SayHelloToTheWorld; begin ShowMessage('Hallo Welt'); end; |
Und diese Funktion kannst du nun ganz einfach für
Action verwenden:
Delphi-Quelltext
1:
| Foo.Action := SayHelloToTheWorld; |
Allerdings weiß ich nicht, ob du Klassenmethoden (also zum Beispiel
Form1.Close) und normale Prozeduren (z.B.
SayHelloToTheWorld) mischen kannst. Für den Fall würde ich einen einfachen Pointer als Typ für
Action vorschlagen:
Delphi-Quelltext
1: 2: 3: 4: 5:
| var Action: Pointer; begin Action := @SayHelloToTheWorld; Action := @Form1.Close; end; |
Und diese Prozedur rufst du wie folgt auf:
Delphi-Quelltext
1: 2: 3:
| asm CALL Action; end; |
Eigentlich ganz einfach, wenn man das Prinzip mal verstanden hat.
ffgorcky - So 24.05.09 21:23
Das würde ich dann eher so machen, dass man da einen Integer-Wert nimmt, der dann genau festlegt, welche Prozedur im Moment aufgerufen werden soll - also so wie
Yogu das ja eigentlich auch schon (fast) [in dem hier 6. Posting] vorgeschlagen hat:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| case welcheProzedur of 0:macheDieseProzedur(); 1:macheJeneProzedur(); 2:macheEineAndereProzedur(); 3:macheNochEineAndereProzedur(); 4:macheKeineAhnungMehrWas(); end; |
Aber ich würde daraus wirklich voneinander unabhängige Prozeduren machen und da ganze nicht einfach nur mit
begin und
end in die
case-Abfrage reinbauen. Das sieht ja bei Dir etwas umfassender aus!
FinnO - So 24.05.09 21:29
@Yogu
hmm okay, diesen(s?) Workaround benutze ich auch gerade, fand ihn aber irgendwie unsauber. Aber wenns nicht anders geht dann... mein Gott mach ichs eben so ;) kann als gelöst betrachtet werden.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!