Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Dynamische Ereignisbehandlng
Klabautermann - Do 10.04.03 14:19
Titel: Dynamische Ereignisbehandlng
Hallo,
ich habe mal eine verständnifrage zum zuweisen von Ereignisbehandlungsmethoden.
Angenommen ich möche einen Menüpunkt eine Methoe zuweisen. Dann geht das wenn es so aussieht:
Quelltext
1: 2: 3: 4: 5: 6: 7: 8:
| tMyObj : class(tObject) private procedure MyClick(Sender: TObject); [...] procedure tMyObj.SetOnClick; begin MyMenu.OnClick := MyClick; end; |
Es funktioniert aber nicht, wenn das ganze so aussieht:
Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| tMyObj : class(tObject) [...] end; // Ende der Klassendefinition
procedure MyClick(Sender: TObject); [...] procedure tMyObj.SetOnClick; begin MyMenu.OnClick := MyClick; end; |
Meine Frage ist nun, warum ist das so? Eigentlich kann es dem Compiler doch völlig schnuppe sein ob die Methode zu dem Objekt gehört oder ob sie "frei" ist.
Neugierig
Klabautermann
Udontknow - Do 10.04.03 14:31
Eigentlich schon, darüber bin ich auch öfters gestolpert.
Zum Begriff "Methode": Eine Methode ist grundsätzlich eine Prozedur oder Funktion eines Objekts, während alles andere dann als normale Prozedur oder Funktion bezeichnet wird.
Tatsache ist, das Platzhalter für Ereignisse (z.B. eine Variable vom Typ TNotifyEvent) nur das eine oder das andere akzeptieren, abhängig davon, ob dieser Ereignistyp mit dem Zusatz "of object" oder nicht deklariert wurde.
Da TNotifyEvent so deklariert ist
Quelltext
1:
| type TNotifyEvent = procedure(Sender:TObject) of object; |
lassen sich nur Methoden zuweisen.
Die Delphihilfe lässt sich darüber leider nicht aus. Der einzige Satz darüber ist folgender:
| Zitat: |
| Zeiger auf Prozeduren sind niemals kompatibel zu Zeigern auf Methoden. |
Cu,
Udontknow
Motzi - Do 10.04.03 14:54
| Udontknow hat folgendes geschrieben: |
Die Delphihilfe lässt sich darüber leider nicht aus. Der einzige Satz darüber ist folgender:
| Zitat: | | Zeiger auf Prozeduren sind niemals kompatibel zu Zeigern auf Methoden. |
|
Ein bisschen mehr steht schon drinnen, man muss nur ein bisschen suchen..!
Ich hab mich relativ ausführlich damit beschäftigt als ich mein Spezialgebiet für meine Matura (österr. Version des Abit) geschrieben hab (mein Thema war "OOP in Delphi").
Methodenzeiger sind anders aufgebaut als Prozedurzeiger. Während Prozedurzeiger nur einen Zeiger darstellen, der an die Stelle im Speicher zeigt an der der Code steht bestehen Methodenzeiger eigentlich aus 2 Pointern. Einer zeigt an die Stelle im Speicher wo der Code ist, während der andre Pointer das Objekt selbst referenziert. Der 2te Pointer stellt also den "Selbst-Zeiger" Self dar. Das ganze kann man relativ leicht nachvollziehn, wenn man eine Methode die den Pointer Self verwendet mal im CPU-Fenster betrachtet. Der Self-Pointer wird immer in eax übergeben, damit die Methode "weiß" zu welchem Objekt sie jetzt eigentlich gehört.
Udontknow - Do 10.04.03 15:05
Ja, das macht Sinn. Wo finde ich denn näheres dazu in der Delphi-Hilfe?
Motzi - Do 10.04.03 15:16
| Udontknow hat folgendes geschrieben: |
| Ja, das macht Sinn. Wo finde ich denn näheres dazu in der Delphi-Hilfe? |
Weiß nicht genau.. hab jetzt grad auch keine Delphi-Hilfe zur Hand, aber schau mal unter Pointer, Methodenzeiger, Prozedurzeiger, ... sowas in der Richtung.
Wenn ich zu Hause bin kann ich dir mehr dazu sagen und eventuell auch einen Link zu meinem Spezialgebiet posten.
Klabautermann - Do 10.04.03 15:39
Hallo,
das war schon mal sehr interessant.
Danke euch beiden.
Gruß
Klabautermann
Motzi - Do 10.04.03 17:55
Hier mal das Zitat aus meiner Delphi-Hilfe:
| Zitat: |
Procedural types allow you to treat procedures and functions as values that can be assigned to variables or passed to other procedures and functions. For example, suppose you define a function called Calc that takes two integer parameters and returns an integer:
Quelltext 1:
| function Calc(X,Y: Integer): Integer; |
You can assign the Calc function to the variable F:
Quelltext 1: 2: 3:
| var F: function(X,Y: Integer): Integer;
F := Calc; |
If you take any procedure or function heading and remove the identifier after the word procedure or function, what?s left is the name of a procedural type. You can use such type names directly in variable declarations (as in the example above) or to declare new types:
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| type
TIntegerFunction = function: Integer; TProcedure = procedure; TStrProc = procedure(const S: string); TMathFunc = function(X: Double): Double; var F: TIntegerFunction; { F is a parameterless function that returns an integer } Proc: TProcedure; { Proc is a parameterless procedure } SP: TStrProc; { SP is a procedure that takes a string parameter } M: TMathFunc; { M is a function that takes a Double (real) parameter
and returns a Double } procedure FuncProc(P: TIntegerFunction); { FuncProc is a procedure whose only parameter is a parameterless integer-valued function } |
The variables above are all procedure pointers?that is, pointers to the address of a procedure or function. If you want to reference a method of an instance object (see Classes and objects), you need to add the words of object to the procedural type name. For example
Quelltext 1: 2: 3: 4:
| type
TMethod = procedure of object; TNotifyEvent = procedure(Sender: TObject) of object; |
These types represent method pointers. A method pointer is really a pair of pointers; the first stores the address of a method, and the second stores a reference to the object the method belongs to. Given the declarations
Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| type
TNotifyEvent = procedure(Sender: TObject) of object; TMainForm = class(TForm) procedure ButtonClick(Sender: TObject); ... end; var MainForm: TMainForm; OnClick: TNotifyEvent |
we could make the following assignment.
Quelltext 1:
| OnClick := MainForm.ButtonClick; |
Two procedural types are compatible if they have
the same calling convention,
the same return value (or no return value), and
the same number of parameters, with identically typed parameters in corresponding positions. (Parameter names do not matter.)
Procedure pointer types are always incompatible with method pointer types. The value nil can be assigned to any procedural type.
Nested procedures and functions (routines declared within other routines) cannot be used as procedural values, nor can predefined procedures and functions. If you want to use a predefined routine like Length as a procedural value, write a wrapper for it:
Quelltext 1: 2: 3: 4: 5:
| function FLength(S: string): Integer;
begin Result := Length(S); end; | |
Motzi - Do 17.04.03 15:52
So, und für diejenigen dies noch interessiert - hier
http://www.x-spy.net/personal kann man sich meine Ausarbeitung über OOP in Delphi runterladen...
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!